first commit

This commit is contained in:
Hamza-Ayed
2026-06-09 08:40:31 +03:00
commit d8901e1a87
3161 changed files with 536187 additions and 0 deletions

View File

@@ -0,0 +1,75 @@
import 'dart:convert';
import 'package:get/get.dart';
import 'package:siro_admin/constant/links.dart';
import 'package:siro_admin/controller/functions/crud.dart';
import '../../print.dart';
class AnalyticsV2Controller extends GetxController {
bool isLoading = true;
Map<String, dynamic> growthData = {};
Map<String, dynamic> revenueData = {};
List<dynamic> topDrivers = [];
@override
void onInit() {
super.onInit();
fetchAllAnalytics();
}
Future<void> fetchAllAnalytics() async {
isLoading = true;
update();
await Future.wait([
fetchGrowth(),
fetchRevenue(),
fetchDriverRanking(),
]);
isLoading = false;
update();
}
Future<void> fetchGrowth() async {
try {
var res = await CRUD().get(link: AppLink.growthV2, payload: {});
if (res != 'failure' && res != null) {
var d = res is String ? jsonDecode(res) : res;
if (d['status'] == 'success') {
growthData = d['data'];
}
}
} catch (e) {
Log.print('Error fetching growth analytics: $e');
}
}
Future<void> fetchRevenue() async {
try {
var res = await CRUD().get(link: AppLink.revenueV2, payload: {});
if (res != 'failure' && res != null) {
var d = res is String ? jsonDecode(res) : res;
if (d['status'] == 'success') {
revenueData = d['data'];
}
}
} catch (e) {
Log.print('Error fetching revenue analytics: $e');
}
}
Future<void> fetchDriverRanking() async {
try {
var res = await CRUD().get(link: AppLink.driverRankingV2, payload: {});
if (res != 'failure' && res != null) {
var d = res is String ? jsonDecode(res) : res;
if (d['status'] == 'success') {
topDrivers = d['data'];
}
}
} catch (e) {
Log.print('Error fetching driver ranking: $e');
}
}
}

View File

@@ -0,0 +1,141 @@
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:local_auth/local_auth.dart';
import '../../constant/colors.dart';
import '../../constant/links.dart';
import '../functions/crud.dart';
class CaptainAdminController extends GetxController {
bool isLoading = false;
Map captainData = {};
Map captain = {};
final captainController = TextEditingController();
final captainPrizeController = TextEditingController();
final titleNotify = TextEditingController();
final bodyNotify = TextEditingController();
final formCaptainKey = GlobalKey<FormState>();
final formCaptainPrizeKey = GlobalKey<FormState>();
Future getCaptainCount() async {
isLoading = true;
update();
var res = await CRUD().get(link: AppLink.getCaptainDetails, payload: {});
var d = jsonDecode(res);
if (d['status'] == 'success') {
captainData = d;
}
isLoading = false;
update();
}
Future deletCaptain() async {
isLoading = true;
update();
var res = await CRUD().get(
link: AppLink.deleteCaptain,
payload: {},
);
var d = jsonDecode(res);
if (d['status'] == 'success') {
captainData = d;
}
isLoading = false;
update();
}
Future find_driver_by_phone(String phone) async {
isLoading = true;
update();
var res = await CRUD().post(
link: AppLink.find_driver_by_phone,
payload: {'phone': "963$phone"},
);
var d = (res);
if (d != 'failure') {
captainData = d;
} else {
captainData = {};
Get.snackbar('Error', 'No captain found with this phone number',
backgroundColor: AppColor.redColor);
}
isLoading = false;
update();
}
Future addCaptainPrizeToWallet() async {
String? paymentId;
//todo link to add wallet to captain
for (var i = 0; i < captainData['message'].length; i++) {
await CRUD().post(link: AppLink.addDriverPaymentPoints, payload: {
'driverID': captainData['message'][i]['id'],
'amount': captainPrizeController.text,
'paymentMethod': 'Prize',
}).then((value) {
paymentId = value['message'].toString();
});
await CRUD().post(link: AppLink.addPassengersWallet, payload: {
'driverID': captainData['message'][i]['id'],
'amount': captainPrizeController.text,
'paymentMethod': 'Prize',
'paymentID': paymentId.toString(),
});
}
Get.back();
}
void addCaptainsPrizeToWalletSecure() async {
try {
// Check if local authentication is available
bool isAvailable = await LocalAuthentication().isDeviceSupported();
if (isAvailable) {
// Authenticate the user
bool didAuthenticate = await LocalAuthentication().authenticate(
localizedReason: 'Use Touch ID or Face ID to confirm payment',
);
if (didAuthenticate) {
// User authenticated successfully, proceed with payment
await addCaptainPrizeToWallet();
Get.snackbar('Prize Added', '', backgroundColor: AppColor.greenColor);
} else {
// Authentication failed, handle accordingly
Get.snackbar('Authentication failed', '',
backgroundColor: AppColor.redColor);
// 'Authentication failed');
}
} else {
// Local authentication not available, proceed with payment without authentication
await addCaptainPrizeToWallet();
Get.snackbar('Prize Added', '', backgroundColor: AppColor.greenColor);
}
} catch (e) {
rethrow;
}
}
Future getCaptains() async {
var res = await CRUD()
.get(link: AppLink.getCaptainDetailsByEmailOrIDOrPhone, payload: {
'driver_id': captainController.text,
'driverEmail': captainController.text,
'driverPhone': captainController.text,
});
var d = jsonDecode(res);
if (d['status'] == 'success') {
captain = d;
}
update();
}
@override
void onInit() {
getCaptainCount();
super.onInit();
}
}

View File

@@ -0,0 +1,70 @@
import 'dart:convert';
import 'package:get/get.dart';
import '../../constant/links.dart';
import '../functions/crud.dart';
class ComplaintController extends GetxController {
var complaintList = [].obs;
var isLoading = false.obs;
var showOnlyDelayed = false.obs;
final CRUD _crud = CRUD();
List<dynamic> get delayedComplaints {
final weekAgo = DateTime.now().subtract(const Duration(days: 7));
return complaintList.where((c) {
if (c['statusComplaint'] == 'Resolved') return false;
try {
final date = DateTime.parse(c['date_filed']);
return date.isBefore(weekAgo);
} catch (e) {
return false;
}
}).toList();
}
@override
void onInit() {
super.onInit();
getComplaints();
}
Future<void> getComplaints() async {
isLoading.value = true;
try {
var response = await _crud.get(link: AppLink.getComplaintAllData);
if (response != null && response != 'failure' && response != 'token_expired') {
var decoded = response is String ? jsonDecode(response) : response;
if (decoded['status'] == "success") {
complaintList.assignAll(decoded['message']);
}
} else {
complaintList.clear();
}
} catch (e) {
Get.snackbar("خطأ", "فشل جلب الشكاوى: $e");
} finally {
isLoading.value = false;
}
}
Future<bool> updateComplaintStatus(String id, String status, String resolution) async {
isLoading.value = true;
try {
var response = await _crud.post(link: "${AppLink.server}/serviceapp/update_complaint.php", payload: {
"id": id,
"statusComplaint": status,
"resolution": resolution,
});
if (response != null && response is Map && response['status'] == "success") {
await getComplaints();
return true;
}
return false;
} catch (e) {
Get.snackbar("خطأ", "فشل تحديث الشكوى: $e");
return false;
} finally {
isLoading.value = false;
}
}
}

View File

@@ -0,0 +1,111 @@
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:siro_admin/constant/links.dart';
import 'package:siro_admin/controller/functions/crud.dart';
import 'package:siro_admin/controller/auth/otp_helper.dart';
import '../../constant/box_name.dart';
import '../../main.dart';
class DashboardController extends GetxController {
bool isLoading = false;
List dashbord = [];
String creditSMS = '0';
final formKey = GlobalKey<FormState>();
final smsText = TextEditingController();
Future getDashBoard() async {
isLoading = true;
update();
// 🔹 Request main dashboard data
var res = await CRUD().get(link: AppLink.getdashbord, payload: {});
print('📡 Main dashboard response: $res');
if (res == 'token_expired') {
print('❌ Admin token expired. Attempting seamless auto-login.');
box.remove(BoxName.jwt);
try {
final otpHelper = Get.put(OtpHelper());
await otpHelper.checkAdminLogin();
} catch (e) {
Get.offAllNamed('/login');
}
return;
}
if (res != 'failure' && res != null) {
try {
var d = res is String ? jsonDecode(res) : res;
print('✅ Decoded main dashboard: ${jsonEncode(d)}');
if (d['status'] == 'success' && d['message'] != null) {
dashbord = d['message'] is List ? d['message'] : [d['message']];
}
} catch (e) {
print('❌ Error parsing main dashboard: $e');
}
} else {
print('❌ Failed to load main dashboard');
}
// 🔹 Request wallet dashboard data
var resPayments = await CRUD().postWallet(
link: AppLink.getPaymentsDashboard,
payload: {},
);
print('💳 Wallet dashboard response: $resPayments');
if (resPayments is Map && resPayments['status'] == 'success') {
var p = resPayments;
print('✅ Decoded wallet dashboard: ${jsonEncode(p)}');
if (dashbord.isNotEmpty &&
p['message'] is List &&
p['message'].isNotEmpty) {
dashbord[0].addAll(p['message'][0]);
} else if (dashbord.isNotEmpty && p['message'] is Map) {
dashbord[0].addAll(p['message']);
}
} else {
print('❌ Failed to load wallet dashboard (or verification required)');
}
// 🔹 Check SMS credit
// var res2 = await CRUD().kazumiSMS(
// link: 'https://sms.kazumi.me/api/sms/check-credit',
// payload: {"username": "Sefer", "password": AK.smsPasswordEgypt},
// );
// creditSMS = res2['credit'];
// print('📱 SMS Credit Response: ${jsonEncode(res2)}');
// print('💰 creditSMS: $creditSMS');
isLoading = false;
update();
}
sendSMSMethod() async {
if (formKey.currentState!.validate()) {
for (var phoneNumber in box.read(BoxName.tokensDrivers)['message']) {
// for (var i = 0; i < 2; i++) {
await CRUD().sendSmsEgypt(
phoneNumber['phone'].toString(),
// box.read(BoxName.tokensDrivers)['message'][i]['phone'].toString(),
smsText.text,
);
// print('CRUD().phoneDriversTest.: ${phoneNumber['phone']}');
Future.delayed(const Duration(microseconds: 20));
}
Get.back();
}
}
@override
void onInit() async {
getDashBoard();
super.onInit();
}
}

View File

@@ -0,0 +1,62 @@
import 'dart:async';
import 'dart:convert';
import 'package:get/get.dart';
import 'package:siro_admin/constant/links.dart';
import 'package:siro_admin/controller/functions/crud.dart';
import '../../print.dart';
class DashboardV2Controller extends GetxController {
bool isLoading = true;
Map<String, dynamic> realtimeData = {};
List<dynamic> smartAlerts = [];
Timer? _timer;
@override
void onInit() {
super.onInit();
fetchRealtimeData();
fetchSmartAlerts();
// Auto refresh every 2 minutes
_timer = Timer.periodic(const Duration(minutes: 2), (timer) {
fetchRealtimeData();
fetchSmartAlerts();
});
}
@override
void onClose() {
_timer?.cancel();
super.onClose();
}
Future<void> fetchRealtimeData() async {
try {
var res = await CRUD().get(link: AppLink.realtimeDashboardV2, payload: {});
if (res != 'failure' && res != null) {
var d = res is String ? jsonDecode(res) : res;
if (d['status'] == 'success') {
realtimeData = d['message'];
isLoading = false;
update();
}
}
} catch (e) {
Log.print('Error fetching realtime dashboard: $e');
}
}
Future<void> fetchSmartAlerts() async {
try {
var res = await CRUD().get(link: AppLink.smartAlertsV2, payload: {});
if (res != 'failure' && res != null) {
var d = res is String ? jsonDecode(res) : res;
if (d['status'] == 'success') {
smartAlerts = d['message'];
update();
}
}
} catch (e) {
Log.print('Error fetching smart alerts: $e');
}
}
}

View File

@@ -0,0 +1,97 @@
import 'dart:convert';
import 'package:get/get.dart';
import '../../constant/links.dart';
import '../functions/crud.dart';
class DriverDocsController extends GetxController {
var pendingDrivers = [].obs;
var isLoading = false.obs;
var isMoreLoading = false.obs;
var hasMore = true.obs;
int _offset = 0;
final int _limit = 10;
final CRUD _crud = CRUD();
@override
void onInit() {
super.onInit();
getPendingDrivers();
}
Future<void> getPendingDrivers({bool refresh = true}) async {
if (refresh) {
isLoading.value = true;
_offset = 0;
hasMore.value = true;
} else {
if (isMoreLoading.value || !hasMore.value) return;
isMoreLoading.value = true;
}
try {
var response = await _crud.post(
link: AppLink.getDriversPending,
payload: {"limit": _limit.toString(), "offset": _offset.toString()},
);
if (response != null && response != 'failure' && response != 'token_expired') {
var decoded = response is String ? jsonDecode(response) : response;
if (decoded['status'] == "success") {
List newItems = decoded['message'] ?? [];
if (refresh) {
pendingDrivers.assignAll(newItems);
} else {
pendingDrivers.addAll(newItems);
}
_offset += newItems.length;
if (newItems.length < _limit) {
hasMore.value = false;
}
}
}
} catch (e) {
Get.snackbar("خطأ", "فشل جلب السائقين: $e");
} finally {
isLoading.value = false;
isMoreLoading.value = false;
}
}
Future<void> loadMore() async {
await getPendingDrivers(refresh: false);
}
Future<Map<String, dynamic>?> getDriverFullDetails(String id) async {
try {
var response = await _crud.get(link: "${AppLink.getDriverDetails}?id=$id");
if (response != null && response != 'failure' && response != 'token_expired') {
var decoded = response is String ? jsonDecode(response) : response;
if (decoded['status'] == "success") {
return decoded['data'];
}
}
} catch (e) {
Get.snackbar("خطأ", "فشل جلب تفاصيل السائق: $e");
}
return null;
}
Future<bool> approveDriver(String id) async {
isLoading.value = true;
try {
var response = await _crud.post(link: AppLink.updateDriverFromAdmin, payload: {
"id": id,
"status": "active",
});
if (response != null && response is Map && response['status'] == "success") {
await getPendingDrivers();
return true;
}
return false;
} catch (e) {
Get.snackbar("خطأ", "فشل اعتماد السائق: $e");
return false;
} finally {
isLoading.value = false;
}
}
}

View File

@@ -0,0 +1,61 @@
import 'dart:convert';
import 'package:get/get.dart';
import 'package:siro_admin/constant/links.dart';
import 'package:siro_admin/controller/functions/crud.dart';
import '../../print.dart';
class FinancialV2Controller extends GetxController {
bool isLoading = true;
Map<String, dynamic> stats = {};
List<dynamic> settlements = [];
@override
void onInit() {
super.onInit();
fetchAllFinancials();
}
Future<void> fetchAllFinancials() async {
isLoading = true;
update();
await Future.wait([
fetchStats(),
fetchSettlements(),
]);
isLoading = false;
update();
}
Future<void> fetchStats() async {
try {
var res =
await CRUD().getWallet(link: AppLink.financialStatsV2, payload: {});
if (res != 'failure' && res != null) {
var d = res is String ? jsonDecode(res) : res;
if (d['status'] == 'success') {
stats = d['data'];
}
}
} catch (e) {
Log.print('Error fetching financial stats: $e');
}
}
Future<void> fetchSettlements() async {
try {
var res =
await CRUD().getWallet(link: AppLink.settlementsV2, payload: {});
if (res != 'failure' && res != null) {
var d = res is String ? jsonDecode(res) : res;
if (d['status'] == 'success') {
settlements = d['data'];
}
}
} catch (e) {
Log.print('Error fetching settlements: $e');
}
}
}

View File

@@ -0,0 +1,25 @@
class InvoiceModel {
final String invoiceNumber;
final String amount;
final String date;
final String name;
final String? imageLink;
InvoiceModel({
required this.invoiceNumber,
required this.amount,
required this.date,
required this.name,
this.imageLink,
});
factory InvoiceModel.fromJson(Map<String, dynamic> json) {
return InvoiceModel(
invoiceNumber: json['invoice_number'],
amount: json['amount'].toString(),
date: json['date'],
name: json['name'],
imageLink: json['image_link'],
);
}
}

View File

@@ -0,0 +1,60 @@
import 'dart:convert';
import 'package:get/get.dart';
import '../../constant/links.dart';
import '../functions/crud.dart';
class KazanController extends GetxController {
var kazanData = {}.obs;
var isLoading = false.obs;
final CRUD _crud = CRUD();
@override
void onInit() {
super.onInit();
getKazan();
}
Future<void> getKazan() async {
isLoading.value = true;
try {
var response = await _crud.get(link: "${AppLink.getKazanPercent}?country=syria");
if (response != null && response != 'failure' && response != 'token_expired') {
var decoded = response is String ? jsonDecode(response) : response;
if (decoded['status'] == "success") {
var message = decoded['message'];
if (message is List && message.isNotEmpty) {
kazanData.value = message[0];
}
}
}
} catch (e) {
Get.snackbar("خطأ", "فشل جلب بيانات التسعير: $e");
} finally {
isLoading.value = false;
}
}
Future<bool> updateKazan(Map<String, dynamic> data) async {
isLoading.value = true;
try {
final String link = data.containsKey('id') ? AppLink.updateKazanPercent : AppLink.addKazanPercent;
Map<String, String> payload = {};
data.forEach((key, value) {
payload[key] = value.toString();
});
var response = await _crud.post(link: link, payload: payload);
if (response != null && response is Map && response['status'] == "success") {
await getKazan();
return true;
}
return false;
} catch (e) {
Get.snackbar("خطأ", "فشل تحديث التسعير: $e");
return false;
} finally {
isLoading.value = false;
}
}
}

View File

@@ -0,0 +1,172 @@
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:local_auth/local_auth.dart';
import '../../constant/colors.dart';
import '../../constant/links.dart';
import '../functions/crud.dart';
class PassengerAdminController extends GetxController {
bool isLoading = false;
Map passengersData = {};
Map passengers = {};
double height = 150;
final formPassKey = GlobalKey<FormState>();
final formPrizeKey = GlobalKey<FormState>();
final titleNotify = TextEditingController();
final bodyNotify = TextEditingController();
final passengerController = TextEditingController();
final passengerPrizeController = TextEditingController();
Future getPassengerCount() async {
isLoading = true;
update();
var res = await CRUD().get(link: AppLink.getPassengerDetails, payload: {});
var d = jsonDecode(res);
if (d['status'] == 'success') {
passengersData = d;
}
isLoading = false;
update();
}
Future addPassengerPrizeToWallet() async {
for (var i = 0; i < passengersData['message'].length; i++) {
await CRUD().post(link: AppLink.addPassengersWallet, payload: {
'passenger_id': passengersData['message'][i]['id'],
'balance': passengerPrizeController.text,
});
}
Get.back();
}
// داخل الـController نفسه
Future<bool> updatePassenger({
required String id, // أو مرّر phoneLookup بدل id لو حاب
String? firstName,
String? lastName,
String? phone,
}) async {
// لا نرسل طلب إذا ما في أي تغيير
if ((firstName == null || firstName.trim().isEmpty) &&
(lastName == null || lastName.trim().isEmpty) &&
(phone == null || phone.trim().isEmpty)) {
return false;
}
// فلتر بسيط للأرقام فقط
// String _normalizePhone(String s) => s.replaceAll(RegExp(r'\D+'), '');
final Map<String, dynamic> payload = {
'id':
id, // لو بدك تستخدم phone_lookup بدل id: احذف هذا وأرسل {'phone_lookup': phoneLookup}
};
if (firstName != null && firstName.trim().isNotEmpty) {
payload['first_name'] = firstName.trim();
}
if (lastName != null && lastName.trim().isNotEmpty) {
payload['last_name'] = lastName.trim();
}
if (phone != null && phone.trim().isNotEmpty) {
payload['phone'] = (phone);
}
// حالة تحميل
isLoading = true;
update();
try {
final res = await CRUD().post(
link: AppLink.admin_update_passenger, // عدّل الرابط حسب اسم مسارك
payload: payload,
);
final d = (res);
final ok = (d['status'] == 'success');
if (ok) {
// (اختياري) حدّث الكاش/الواجهة — مثلاً أعد الجلب
Get.snackbar('Update successful',
d['message']?.toString() ?? 'Passenger updated successfully',
backgroundColor: AppColor.greenColor);
// await getPassengerCount(); // أو حدّث passengersData محليًا إذا متاح
} else {
// (اختياري) أظهر رسالة خطأ
// Get.snackbar('Update failed', d['message']?.toString() ?? 'Unknown error');
}
return ok;
} catch (e) {
// Get.snackbar('Error', e.toString());
return false;
} finally {
isLoading = false;
update();
}
}
void addPassengerPrizeToWalletSecure() async {
try {
// Check if local authentication is available
bool isAvailable = await LocalAuthentication().isDeviceSupported();
if (isAvailable) {
// Authenticate the user
bool didAuthenticate = await LocalAuthentication().authenticate(
localizedReason: 'Use Touch ID or Face ID to confirm payment',
);
if (didAuthenticate) {
// User authenticated successfully, proceed with payment
await addPassengerPrizeToWallet();
Get.snackbar('Prize Added', '', backgroundColor: AppColor.greenColor);
} else {
// Authentication failed, handle accordingly
Get.snackbar('Authentication failed', '',
backgroundColor: AppColor.redColor);
// 'Authentication failed');
}
} else {
// Local authentication not available, proceed with payment without authentication
await addPassengerPrizeToWallet();
Get.snackbar('Prize Added', '', backgroundColor: AppColor.greenColor);
}
} catch (e) {
rethrow;
}
}
Future getPassengers() async {
var res = await CRUD().get(link: AppLink.getPassengerbyEmail, payload: {
'passengerEmail': passengerController.text,
'passengerId': passengerController.text,
'passengerphone': passengerController.text,
});
var d = jsonDecode(res);
if (d['status'] == 'success') {
passengers = d;
}
update();
}
changeHeight() {
if (passengers.isEmpty) {
height = 0;
update();
}
height = 150;
update();
}
void clearPlaces() {
passengers = {};
update();
}
@override
void onInit() {
getPassengerCount();
super.onInit();
}
}

View File

@@ -0,0 +1,83 @@
import 'dart:convert';
import 'package:get/get.dart';
import '../../constant/links.dart';
import '../functions/crud.dart';
class PromoController extends GetxController {
var promoList = [].obs;
var isLoading = false.obs;
final CRUD _crud = CRUD();
@override
void onInit() {
super.onInit();
getPromos();
}
Future<void> getPromos() async {
isLoading.value = true;
try {
var response = await _crud.get(link: AppLink.getPassengersPromo);
if (response != null && response != 'failure' && response != 'token_expired') {
var decoded = response is String ? jsonDecode(response) : response;
if (decoded['status'] == "success") {
promoList.assignAll(decoded['message']);
}
} else {
promoList.clear();
}
} catch (e) {
Get.snackbar("خطأ", "فشل جلب أكواد الخصم: $e");
} finally {
isLoading.value = false;
}
}
Future<bool> addPromo(Map<String, dynamic> data) async {
isLoading.value = true;
try {
var response = await _crud.post(link: AppLink.addPassengersPromo, payload: data);
if (response != null && response is Map && response['status'] == "success") {
await getPromos();
return true;
}
return false;
} catch (e) {
Get.snackbar("خطأ", "فشل إضافة كود الخصم: $e");
return false;
} finally {
isLoading.value = false;
}
}
Future<bool> deletePromo(String id) async {
try {
var response = await _crud.post(link: AppLink.deletePassengersPromo, payload: {"id": id});
if (response != null && response is Map && response['status'] == "success") {
await getPromos();
return true;
}
return false;
} catch (e) {
Get.snackbar("خطأ", "فشل حذف كود الخصم: $e");
return false;
}
}
Future<bool> updatePromo(Map<String, dynamic> data) async {
isLoading.value = true;
try {
var response = await _crud.post(link: AppLink.updatePassengersPromo, payload: data);
if (response != null && response is Map && response['status'] == "success") {
await getPromos();
return true;
}
return false;
} catch (e) {
Get.snackbar("خطأ", "فشل تحديث كود الخصم: $e");
return false;
} finally {
isLoading.value = false;
}
}
}

View File

@@ -0,0 +1,102 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../../constant/links.dart';
import '../functions/crud.dart';
class QualityController extends GetxController {
bool isLoading = false;
List driversBlacklist = [];
List passengersBlacklist = [];
Map scorecardData = {};
Future<void> fetchBlacklist() async {
isLoading = true;
update();
try {
var res = await CRUD().post(
link: AppLink.blacklistManager,
payload: {"action_type": "get_all"},
);
if (res is Map && res['status'] == 'success') {
driversBlacklist = res['message']['drivers'] ?? [];
passengersBlacklist = res['message']['passengers'] ?? [];
} else {
Get.snackbar("Error", "Failed to fetch blacklist");
}
} catch (e) {
Get.snackbar("Error", "Network error");
} finally {
isLoading = false;
update();
}
}
Future<void> unblockDriver(String phone) async {
try {
var res = await CRUD().post(
link: AppLink.blacklistManager,
payload: {
"action_type": "unblock_driver",
"phone": phone,
},
);
if (res is Map && res['status'] == 'success') {
Get.snackbar("Success", "Driver unblocked successfully");
fetchBlacklist(); // Refresh
} else {
Get.snackbar("Error", res['message'] ?? "Failed to unblock driver");
}
} catch (e) {
Get.snackbar("Error", "Network error");
}
}
Future<void> unblockPassenger(String phoneNormalized) async {
try {
var res = await CRUD().post(
link: AppLink.blacklistManager,
payload: {
"action_type": "unblock_passenger",
"phone_normalized": phoneNormalized,
},
);
if (res is Map && res['status'] == 'success') {
Get.snackbar("Success", "Passenger unblocked successfully");
fetchBlacklist(); // Refresh
} else {
Get.snackbar("Error", res['message'] ?? "Failed to unblock passenger");
}
} catch (e) {
Get.snackbar("Error", "Network error");
}
}
Future<void> fetchDriverScorecard(String driverId) async {
isLoading = true;
update();
try {
var res = await CRUD().post(
link: AppLink.driverScorecard,
payload: {"driver_id": driverId},
);
if (res is Map && res['status'] == 'success') {
scorecardData = res['message'];
} else {
Get.snackbar("Error", "Failed to fetch scorecard");
scorecardData = {};
}
} catch (e) {
Get.snackbar("Error", "Network error");
scorecardData = {};
} finally {
isLoading = false;
update();
}
}
@override
void onInit() {
super.onInit();
// fetchBlacklist() can be called when opening the page
}
}

View File

@@ -0,0 +1,590 @@
import 'dart:convert';
import 'dart:ffi';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:http/http.dart' as http;
import '../../constant/api_key.dart';
import '../../constant/box_name.dart';
import '../../constant/colors.dart';
import '../../constant/info.dart';
import '../../constant/links.dart';
import '../../constant/style.dart';
import '../../main.dart';
import '../functions/crud.dart';
class RegisterCaptainController extends GetxController {
bool isDriverSaved = false;
bool isCarSaved = false;
Map<String, dynamic>? arguments;
String? driverId;
String? email;
String? phone;
@override
void onInit() {
super.onInit();
arguments = Get.arguments;
initArguments();
}
void driveInit() {
arguments = Get.arguments;
initArguments();
}
void initArguments() {
if (arguments != null) {
driverId = arguments!['driverId'];
email = arguments!['email'];
phone = arguments!['phone_number'];
} else {
print('Arguments are null');
}
}
Map<String, dynamic> responseMap = {};
Map<String, dynamic> responseCarLicenseMapJordan = {};
Map<String, dynamic> responseBackCarLicenseMap = {};
Map<String, dynamic> responseIdCardMap = {};
Map<String, dynamic> responseIdCardDriverEgyptBack = {};
Map<String, dynamic> responseForComplaint = {};
Map<String, dynamic> responseIdCardDriverEgyptFront = {};
Map<String, dynamic> responseIdEgyptFront = {};
Map<String, dynamic> responseCriminalRecordEgypt = {};
Map<String, dynamic> responseIdEgyptBack = {};
Map<String, dynamic> responseIdEgyptDriverLicense = {};
String? responseIdCardDriverEgypt1;
bool isloading = false;
var image;
DateTime now = DateTime.now();
bool isLoading = false;
Future allMethodForAI(String prompt, imagePath, driverID) async {
isLoading = true;
update();
var extractedString = await CRUD().arabicTextExtractByVisionAndAI(
imagePath: imagePath, driverID: driverID);
var json = jsonDecode(extractedString);
var textValues = extractTextFromLines(json);
// await Get.put(AI()).geminiAiExtraction(prompt, textValues, imagePath);
await Get.put(RegisterCaptainController())
.anthropicAI(textValues, prompt, imagePath);
isLoading = false;
update();
}
String extractTextFromLines(Map<String, dynamic> jsonData) {
final readResult = jsonData['readResult'];
final blocks = readResult['blocks'];
final StringBuffer buffer = StringBuffer();
for (final block in blocks) {
final lines = block['lines'];
for (final line in lines) {
final text = line['text'];
buffer.write(text);
buffer.write('\n');
}
}
return buffer.toString().trim();
}
List driverNotCompleteRegistration = [];
getDriverNotCompleteRegistration() async {
var res = await CRUD()
.get(link: AppLink.getDriverNotCompleteRegistration, payload: {});
if (res != 'failure') {
var d = jsonDecode(res)['message'];
driverNotCompleteRegistration = d;
update();
} else {
Get.snackbar(res, '');
}
}
final today = DateTime.now();
Future<void> addDriverAndCarEgypt() async {
final expiryDate = responseIdEgyptDriverLicense['expiry_date'];
final expiryDateTime = DateTime.tryParse(expiryDate);
final isExpired = expiryDateTime != null && expiryDateTime.isBefore(today);
final taxExpiryDate = responseIdCardDriverEgyptBack['tax_expiry'];
// Get the inspection date from the response
final inspectionDate = responseIdCardDriverEgyptBack['inspection_date'];
final year = int.parse(inspectionDate.split('-')[0]);
// Try parsing the tax expiry date. If it fails, set it to null.
final taxExpiryDateTime = DateTime.tryParse(taxExpiryDate ?? '');
final isExpiredCar =
taxExpiryDateTime != null && taxExpiryDateTime.isBefore(today);
// Check if the inspection date is before today
final inspectionDateTime = DateTime(year, 1, 1);
final isInspectionExpired = inspectionDateTime.isBefore(today);
if (isExpiredCar || isInspectionExpired) {
Get.defaultDialog(
title: 'Expired Drivers License'.tr,
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
const Icon(Icons.warning, size: 48, color: Colors.red),
const SizedBox(height: 16),
Text(
'Your drivers license and/or car tax has expired. Please renew them before proceeding.'
.tr,
textAlign: TextAlign.center,
style: AppStyle.title,
),
const SizedBox(height: 16),
IconButton(
onPressed: () async {
// await Get.find<TextToSpeechController>().speakText(
// 'Your drivers license and/or car tax has expired. Please renew them before proceeding.'
// .tr,
// );
},
icon: const Icon(Icons.volume_up),
),
],
),
actions: [
TextButton(
onPressed: () {
Get.back();
},
child: const Text('OK'),
),
],
);
} else if (isExpired) {
Get.defaultDialog(
title: 'Expired Drivers License'.tr,
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
const Icon(Icons.warning, size: 48, color: Colors.red),
const SizedBox(height: 16),
Text(
'Your drivers license has expired. Please renew it before proceeding.'
.tr,
textAlign: TextAlign.center,
style: AppStyle.title,
),
const SizedBox(height: 16),
IconButton(
onPressed: () async {
// await Get.find<TextToSpeechController>().speakText(
// 'Your drivers license has expired. Please renew it before proceeding.'
// .tr,
// );
},
icon: const Icon(Icons.volume_up),
),
],
),
actions: [
TextButton(
onPressed: () {
Get.back();
},
child: const Text('OK'),
),
],
);
} else if (responseIdEgyptDriverLicense['national_number']
.toString()
.substring(0, 12) !=
responseIdEgyptBack['nationalID'].toString().substring(0, 12)) {
Get.defaultDialog(
barrierDismissible: false,
title: 'ID Mismatch',
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
const Icon(Icons.warning, size: 48, color: Colors.red),
const SizedBox(height: 16),
Text(
'The national number on your drivers license does not match the one on your ID document. Please verify and provide the correct documents.'
.tr,
textAlign: TextAlign.center,
style: AppStyle.title,
),
const SizedBox(height: 16),
IconButton(
onPressed: () async {
// await Get.find<TextToSpeechController>().speakText(
// 'The national number on your drivers license does not match the one on your ID document. Please verify and provide the correct documents.',
// );
},
icon: const Icon(Icons.volume_up),
),
],
),
actions: [
TextButton(
onPressed: () {
Get.back();
},
child: const Text('OK'),
),
],
);
}
// else if (responseCriminalRecordEgypt['FullName'] !=
// responseIdEgyptDriverLicense['name_arabic']) {
// Get.defaultDialog(
// barrierDismissible: false,
// title: 'Criminal Record Mismatch',
// content: Column(
// mainAxisSize: MainAxisSize.min,
// children: [
// const Icon(Icons.warning, size: 48, color: Colors.red),
// const SizedBox(height: 16),
// Text(
// 'The full name on your criminal record does not match the one on your drivers license. Please verify and provide the correct documents.'
// .tr,
// textAlign: TextAlign.center,
// style: AppStyle.title,
// ),
// const SizedBox(height: 16),
// IconButton(
// onPressed: () async {
// await Get.find<TextToSpeechController>().speakText(
// 'The full name on your criminal record does not match the one on your drivers license. Please verify and provide the correct documents.'
// .tr,
// );
// },
// icon: const Icon(Icons.volume_up),
// ),
// ],
// ),
// actions: [
// TextButton(
// onPressed: () {
// Get.back();
// },
// child: const Text('OK'),
// ),
// ],
// );
// }
else {
await addDriverEgypt();
await addRegistrationCarEgypt();
if (isCarSaved && isDriverSaved) {
Get.snackbar('added', '',
backgroundColor:
AppColor.greenColor); // Get.offAll(() => HomeCaptain());
// Get.offAll(() => HomeCaptain());
}
}
}
Future<void> addDriverEgypt() async {
isLoading = true;
update();
var payload = {
'first_name': responseIdEgyptDriverLicense['firstName']?.toString() ??
'Not specified',
'last_name': responseIdEgyptDriverLicense['lastName']?.toString() ??
'Not specified',
'email': email?.toString() ?? 'Not specified',
'phone': phone?.toString() ?? 'Not specified',
'id': driverId?.toString() ?? 'Not specified',
'password': '123456',
'gender': responseIdEgyptBack['gender']?.toString() ?? 'Not specified',
'license_type':
responseIdEgyptDriverLicense['license_type']?.toString() ??
'Not specified',
'national_number':
responseIdEgyptBack['nationalID']?.toString() ?? 'Not specified',
'name_arabic': responseIdEgyptDriverLicense['name_arabic']?.toString() ??
'Not specified',
'name_english':
responseIdEgyptDriverLicense['name_english']?.toString() ??
'Not specified',
'issue_date': responseIdEgyptDriverLicense['issue_date']?.toString() ??
'Not specified',
'expiry_date': responseIdEgyptDriverLicense['expiry_date']?.toString() ??
'Not specified',
'license_categories': responseIdEgyptDriverLicense['license_categories']
is List
? responseIdEgyptDriverLicense['license_categories'].join(', ')
: responseIdEgyptDriverLicense['license_categories']?.toString() ??
'Not specified',
'address': responseIdEgyptFront['address']?.toString() ?? 'Not specified',
'card_id': responseIdEgyptFront['card_id']?.toString() ?? 'Not specified',
'occupation':
responseIdEgyptBack['occupation']?.toString() ?? 'Not specified',
'education':
responseIdEgyptBack['occupation']?.toString() ?? 'Not specified',
'licenseIssueDate':
responseIdEgyptDriverLicense['issue_date']?.toString() ??
'Not specified',
'religion':
responseIdEgyptBack['religion']?.toString() ?? 'Not specified',
'status': 'yet',
'birthdate': responseIdEgyptFront['dob']?.toString() ?? 'Not specified',
'maritalStatus':
responseIdEgyptBack['maritalStatus']?.toString() ?? 'Not specified',
'site': responseIdEgyptDriverLicense['address']?.toString() ??
'Not specified',
'employmentType':
responseIdEgyptDriverLicense['employmentType']?.toString() ??
'Not specified',
};
var res = await CRUD().post(link: AppLink.signUpCaptin, payload: payload);
var status1 = jsonDecode(res);
isLoading = false;
update();
// Handle response
if (status1['status'] == 'success') {
isDriverSaved = true;
Get.snackbar('Success', 'Driver data saved successfully',
backgroundColor: AppColor.greenColor);
} else {
Get.snackbar('Error', 'Failed to save driver data',
backgroundColor: Colors.red);
}
}
addCriminalDeocuments() async {
var res = await CRUD().post(link: AppLink.addCriminalDocuments, payload: {
"driverId": box.read(BoxName.driverID),
"IssueDate": responseCriminalRecordEgypt['IssueDate'],
"InspectionResult": responseCriminalRecordEgypt['InspectionResult'],
});
if (res != 'failure') {
Get.snackbar('uploaded sucssefuly'.tr, '');
}
}
Future addRegistrationCarEgypt() async {
try {
isLoading = true;
update();
var res = await CRUD().post(link: AppLink.addRegisrationCar, payload: {
'driverID': driverId,
'vin': responseIdCardDriverEgyptBack['chassis'].toString(),
'car_plate': responseIdCardDriverEgyptFront['car_plate'].toString(),
'make': responseIdCardDriverEgyptBack['make'].toString(),
'model': responseIdCardDriverEgyptBack['model'],
'year': responseIdCardDriverEgyptBack['year'].toString(),
'expiration_date':
responseIdCardDriverEgyptFront['LicenseExpirationDate'].toString(),
'color': responseIdCardDriverEgyptBack['color'],
'owner': responseIdCardDriverEgyptFront['owner'],
'color_hex': responseIdCardDriverEgyptBack['color_hex'].toString(),
'address': responseIdCardDriverEgyptFront['address'].toString(),
'displacement': responseIdCardDriverEgyptBack['engine'].toString(),
'fuel': responseIdCardDriverEgyptBack['fuel'].toString(),
'registration_date':
'${responseIdCardDriverEgyptBack['inspection_date']}',
});
isLoading = false;
update();
var status = jsonDecode(res);
if (status['status'] == 'success') {
isCarSaved = true;
Get.snackbar('Success', 'message',
backgroundColor: AppColor.greenColor);
}
} catch (e) {}
}
Future getComplaintDataToAI() async {
var res = await CRUD().get(
link: AppLink.getComplaintAllDataForDriver,
payload: {'driver_id': driverId.toString()},
);
if (res != 'failure') {
var d = jsonDecode(res)['message'];
return d;
} else {
return [
{'data': 'no data'}
];
}
}
Future<dynamic> anthropicAIForComplaint() async {
var dataComplaint = await getComplaintDataToAI();
var messagesData = [
{
"role": "user",
"content": [
{
"type": "text",
"text": "$dataComplaint ${AppInformation.complaintPrompt} "
}
]
}
];
var requestBody = jsonEncode({
"model": "claude-3-haiku-20240307",
"max_tokens": 1024,
"temperature": 0,
"system": "Json output only without any additional ",
"messages": messagesData,
});
final response = await http.post(
Uri.parse('https://api.anthropic.com/v1/messages'),
headers: {
'x-api-key': AK.anthropicAIkeySeferNew,
'anthropic-version': '2023-06-01',
'content-type': 'application/json'
},
body: requestBody,
);
if (response.statusCode == 200) {
var responseData = jsonDecode(utf8.decode(response.bodyBytes));
// Process the responseData as needed
responseForComplaint = jsonDecode(responseData['content'][0]['text']);
}
}
Future<dynamic> anthropicAI(
String payload, String prompt, String idType) async {
var messagesData = [
{
"role": "user",
"content": [
{"type": "text", "text": "$payload $prompt"}
]
}
];
var requestBody = jsonEncode({
"model": "claude-3-haiku-20240307",
"max_tokens": 1024,
"temperature": 0,
"system": "Json output only without any additional ",
"messages": messagesData,
});
final response = await http.post(
Uri.parse('https://api.anthropic.com/v1/messages'),
headers: {
'x-api-key': AK.anthropicAIkeySeferNew,
'anthropic-version': '2023-06-01',
'content-type': 'application/json'
},
body: requestBody,
);
if (response.statusCode == 200) {
var responseData = jsonDecode(utf8.decode(response.bodyBytes));
// Process the responseData as needed
if (idType == 'car_back') {
responseIdCardDriverEgyptBack =
jsonDecode(responseData['content'][0]['text']);
} else if (idType == 'car_front') {
responseIdCardDriverEgyptFront =
jsonDecode(responseData['content'][0]['text']);
} else if (idType == 'id_front') {
responseIdEgyptFront = jsonDecode(responseData['content'][0]['text']);
} else if (idType == 'id_back') {
responseIdEgyptBack = jsonDecode(responseData['content'][0]['text']);
} else if (idType == 'driver_license') {
responseIdEgyptDriverLicense =
jsonDecode(responseData['content'][0]['text']);
} else if (idType == 'criminalRecord') {
responseCriminalRecordEgypt =
jsonDecode(responseData['content'][0]['text']);
}
update();
return responseData.toString();
}
return responseIdCardDriverEgyptBack.toString();
}
Future<void> geminiAiExtraction(String prompt, payload, String idType) async {
var requestBody = jsonEncode({
"contents": [
{
"parts": [
{"text": "$payload $prompt"}
]
}
],
"generationConfig": {
"temperature": 1,
"topK": 64,
"topP": 0.95,
"maxOutputTokens": 8192,
"stopSequences": []
},
"safetySettings": [
{
"category": "HARM_CATEGORY_HARASSMENT",
"threshold": "BLOCK_MEDIUM_AND_ABOVE"
},
{
"category": "HARM_CATEGORY_HATE_SPEECH",
"threshold": "BLOCK_MEDIUM_AND_ABOVE"
},
{
"category": "HARM_CATEGORY_SEXUALLY_EXPLICIT",
"threshold": "BLOCK_MEDIUM_AND_ABOVE"
},
{
"category": "HARM_CATEGORY_DANGEROUS_CONTENT",
"threshold": "BLOCK_MEDIUM_AND_ABOVE"
}
]
});
final response = await http.post(
Uri.parse(
// 'https://generativelanguage.googleapis.com/v1beta/models/gemini-pro-vision:generateContent?key=${AK.geminiApi}'),
// 'https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-pro-latest:generateContent?key=${AK.geminiApi}'),
'https://generativelanguage.googleapis.com/v1beta/models/gemini-1.0-pro:generateContent?key=${AK.geminiApi}'),
headers: {'Content-Type': 'application/json'},
body: requestBody,
);
if (response.statusCode == 200) {
var responseData = jsonDecode(response.body);
// Process the responseData as needed
var result = responseData['candidates'][0]['content']['parts'][0]['text'];
RegExp regex = RegExp(r"```json([^`]*)```");
String? jsonString =
regex.firstMatch(responseData.toString())?.group(1)?.trim();
if (jsonString != null) {
// Convert the JSON object to a String
jsonString = jsonEncode(json.decode(jsonString));
if (idType == 'car_back') {
responseIdCardDriverEgyptBack = jsonDecode(jsonString);
} else if (idType == 'car_front') {
responseIdCardDriverEgyptFront = jsonDecode(jsonString);
} else if (idType == 'id_front') {
responseIdEgyptFront = jsonDecode(jsonString);
} else if (idType == 'id_back') {
responseIdEgyptBack = jsonDecode(jsonString);
} else if (idType == 'driver_license') {
responseIdEgyptDriverLicense = jsonDecode(jsonString);
}
update();
} else {
Get.snackbar('Error', "JSON string not found",
backgroundColor: AppColor.redColor);
}
// Rest of your code...
} else {}
}
}

View File

@@ -0,0 +1,73 @@
import 'dart:convert';
import 'package:fl_chart/fl_chart.dart';
import 'package:get/get.dart';
import '../../constant/links.dart';
import '../../models/model/admin/monthly_ride.dart';
import '../functions/crud.dart';
class RideAdminController extends GetxController {
bool isLoading = false;
late List<MonthlyDataModel> rideData;
late Map<String, dynamic> jsonResponse;
List<dynamic> ridesDetails = [];
var chartData;
// late List<ChartDataS> chartDatasync;
Future getRidesAdminDash() async {
isLoading = true;
update();
var res = await CRUD().get(link: AppLink.getRidesPerMonth, payload: {});
jsonResponse = jsonDecode(res);
rideData = (jsonResponse['message'] as List)
.map((item) => MonthlyDataModel.fromJson(item))
.toList();
chartData = rideData
.map((data) => FlSpot(data.day.toDouble(), data.ridesCount.toDouble()))
.toList();
// chartDatasync = (jsonResponse['message'] as List)
// .map((item) => ChartDataS(
// item['year'],
// item['month'],
// item['day'],
// item['rides_count'],
// ))
// .toList();
isLoading = false;
update();
}
Future getRidesDetails() async {
// isLoading = true;
// update();
var res = await CRUD().get(link: AppLink.getRidesDetails, payload: {});
var d = jsonDecode(res);
ridesDetails = d['message'];
// isLoading = false;
// update();
}
@override
void onInit() async {
List<Future> initializationTasks = [
getRidesAdminDash(),
getRidesDetails(),
];
// cameras = await availableCameras();
await Future.wait(initializationTasks);
super.onInit();
}
}
// class ChartDataS {
// ChartDataS(this.year, this.month, this.day, this.ridesCount);
// final int year;
// final int month;
// final int day;
// final int ridesCount;
// }

View File

@@ -0,0 +1,62 @@
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:siro_admin/constant/links.dart';
import 'package:siro_admin/controller/functions/crud.dart';
import '../../print.dart';
class SecurityV2Controller extends GetxController {
bool isLoading = true;
List<dynamic> auditLogs = [];
@override
void onInit() {
super.onInit();
fetchAuditLogs();
}
Future<void> fetchAuditLogs() async {
isLoading = true;
update();
try {
Log.print('Fetching from: ${AppLink.auditLogsV2}');
var res = await CRUD().get(link: AppLink.auditLogsV2, payload: {});
Log.print('Raw audit res type: ${res.runtimeType} | value: $res');
if (res == 'failure' || res == 'token_expired') {
Log.print('CRUD returned: $res');
Get.snackbar("خطأ بالاتصال", "السيرفر أرجع: $res",
backgroundColor: const Color(0x88FF0000),
colorText: const Color(0xFFFFFFFF));
auditLogs = [];
} else if (res != null) {
var d = res is String ? jsonDecode(res) : res;
Log.print('Decoded audit response: $d');
if (d['status'] == 'success') {
var message = d['message'];
if (message is List) {
auditLogs = message;
Log.print('Loaded ${auditLogs.length} audit logs');
} else {
auditLogs = [];
Log.print('message is not List: ${message.runtimeType}');
}
} else {
Log.print('Status not success: ${d['status']}');
Get.snackbar("خطأ من السيرفر", "${d['message'] ?? d['status']}",
backgroundColor: const Color(0x88FF0000),
colorText: const Color(0xFFFFFFFF));
}
}
} catch (e) {
Log.print('Error fetching audit logs: $e');
Get.snackbar("خطأ برمجي", "$e",
backgroundColor: const Color(0x88FF0000),
colorText: const Color(0xFFFFFFFF));
}
isLoading = false;
update();
}
}

View File

@@ -0,0 +1,81 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../../constant/links.dart';
import '../functions/crud.dart';
import '../functions/device_info.dart';
import '../../views/widgets/snackbar.dart';
class StaffController extends GetxController {
final CRUD _crud = CRUD();
final formKey = GlobalKey<FormState>();
// التكست كنترولرز
final nameController = TextEditingController();
final phoneController = TextEditingController();
final emailController = TextEditingController();
final passwordController = TextEditingController();
final birthdateController = TextEditingController();
String selectedGender = 'Male';
String selectedRole = 'service'; // 'admin' or 'service'
bool isLoading = false;
Future<void> registerStaff() async {
if (!formKey.currentState!.validate()) return;
isLoading = true;
update();
try {
// ملاحظة: لا نأخذ بصمة جهاز الأدمن هنا، بل نتركها فارغة ليقوم الموظف بربطها عند أول دخول له
String fingerprint = "";
var response = await _crud.post(
link: AppLink.addStaff,
payload: {
"name": nameController.text.trim(),
"phone": phoneController.text.trim(),
"email": emailController.text.trim(),
"password": passwordController.text.trim(),
"role": selectedRole,
"gender": selectedGender,
"birthdate": birthdateController.text.trim(),
"fingerprint": fingerprint,
"site": "main", // القيمة الافتراضية للفرع
},
);
if (response != "failure") {
mySnackbarSuccess('تمت إضافة الموظف بنجاح');
_clearFields();
Get.back();
} else {
mySnackeBarError('فشل في إضافة الموظف. يرجى المحاولة لاحقاً');
}
} catch (e) {
mySnackeBarError('حدث خطأ: $e');
} finally {
isLoading = false;
update();
}
}
void _clearFields() {
nameController.clear();
phoneController.clear();
emailController.clear();
passwordController.clear();
birthdateController.clear();
}
@override
void onClose() {
nameController.dispose();
phoneController.dispose();
emailController.dispose();
passwordController.dispose();
birthdateController.dispose();
super.onClose();
}
}

View File

@@ -0,0 +1,512 @@
import 'dart:convert';
import 'package:fl_chart/fl_chart.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:intl/intl.dart';
import '../../constant/links.dart';
import '../../print.dart';
import '../functions/crud.dart';
// ══════════════════════════════════════════════════════════════
// MODEL: Represents one employee's full data for a period
// ══════════════════════════════════════════════════════════════
class EmployeeChartData {
final String name;
final Color color;
final List<FlSpot> notesSpots;
final List<FlSpot> callsSpots;
const EmployeeChartData({
required this.name,
required this.color,
required this.notesSpots,
required this.callsSpots,
});
int get totalNotes => notesSpots.fold(0, (sum, s) => sum + s.y.toInt());
int get totalCalls => callsSpots.fold(0, (sum, s) => sum + s.y.toInt());
EmployeeChartData copyWith({
List<FlSpot>? notesSpots,
List<FlSpot>? callsSpots,
}) {
return EmployeeChartData(
name: name,
color: color,
notesSpots: notesSpots ?? this.notesSpots,
callsSpots: callsSpots ?? this.callsSpots,
);
}
}
// ══════════════════════════════════════════════════════════════
// MODEL: Employment activation stats per employee
// ══════════════════════════════════════════════════════════════
class EmploymentStat {
final String name;
final int count;
final Color color;
const EmploymentStat({
required this.name,
required this.count,
required this.color,
});
}
// ══════════════════════════════════════════════════════════════
// CONTROLLER
// ══════════════════════════════════════════════════════════════
class StaticController extends GetxController {
// ─── Color Palette for Dynamic Employees ───────────────────
static const List<Color> _employeeColors = [
Color(0xFF00D4AA), // teal
Color(0xFF82AAFF), // blue
Color(0xFFFFCB6B), // amber
Color(0xFFC792EA), // purple
Color(0xFFFF5370), // red
Color(0xFFC3E88D), // green
Color(0xFFF07178), // coral
Color(0xFF89DDFF), // cyan
];
Color _colorForIndex(int i) => _employeeColors[i % _employeeColors.length];
// ─── Date & State ───────────────────────────────────────────
DateTime? startDate = DateTime(DateTime.now().year, DateTime.now().month, 1);
DateTime? endDate =
DateTime(DateTime.now().year, DateTime.now().month + 1, 0);
DateTime? compareStartDate;
DateTime? compareEndDate;
bool isComparing = false;
bool isLoading = false;
// ─── Daily Notes State ─────────────────────────────────────
bool isLoadingNotes = false;
List<dynamic> dailyNotesList = [];
// ─── Main Chart Data ───────────────────────────────────────
List<FlSpot> chartDataPassengers = [];
List<FlSpot> chartDataDrivers = [];
List<FlSpot> chartDataRides = [];
List<FlSpot> chartDataDriversMatchingNotes = [];
List<FlSpot> chartDataPassengersCompare = [];
List<FlSpot> chartDataDriversCompare = [];
List<FlSpot> chartDataRidesCompare = [];
List<FlSpot> chartDataDriversMatchingNotesCompare = [];
// ─── 🔥 DYNAMIC Employee Data ─────────────────────────────
// Key = employee name (from server), Value = their chart data
Map<String, EmployeeChartData> employeeData = {};
Map<String, EmployeeChartData> employeeDataCompare = {};
// Set of all known employee names (union of current + compare)
Set<String> get allEmployeeNames => {
...employeeData.keys,
...employeeDataCompare.keys,
};
// ─── Employment Stats ──────────────────────────────────────
List<EmploymentStat> employmentStatsList = [];
// ─── Totals ────────────────────────────────────────────────
String totalMonthlyPassengers = '0';
String totalMonthlyRides = '0';
String totalMonthlyDrivers = '0';
// ─── Raw Lists ─────────────────────────────────────────────
List staticList = [];
// ─── Color Registry (stable across rebuilds) ───────────────
final Map<String, Color> _employeeColorRegistry = {};
Color _getOrAssignColor(String name) {
if (!_employeeColorRegistry.containsKey(name)) {
_employeeColorRegistry[name] =
_colorForIndex(_employeeColorRegistry.length);
}
return _employeeColorRegistry[name]!;
}
@override
void onInit() {
super.onInit();
getAll();
}
// ─── Helpers ───────────────────────────────────────────────
double get daysInPeriod {
if (startDate == null || endDate == null) return 31;
return endDate!.difference(startDate!).inDays + 1.0;
}
String get currentDateString {
if (startDate == null || endDate == null) return "";
return "${DateFormat('yyyy-MM-dd').format(startDate!)} : "
"${DateFormat('yyyy-MM-dd').format(endDate!)}";
}
String get compareDateString {
if (compareStartDate == null || compareEndDate == null) return "";
return "${DateFormat('yyyy-MM-dd').format(compareStartDate!)} : "
"${DateFormat('yyyy-MM-dd').format(compareEndDate!)}";
}
// ─── Date Actions ──────────────────────────────────────────
void updateDateRange(DateTime start, DateTime end) {
startDate = start;
endDate = end;
if (isComparing) _calculateCompareDates();
getAll();
update();
}
void _calculateCompareDates() {
if (startDate == null || endDate == null) return;
Duration duration = endDate!.difference(startDate!);
compareEndDate = startDate!.subtract(const Duration(days: 1));
compareStartDate = compareEndDate!.subtract(duration);
}
Future<void> toggleComparison() async {
isComparing = !isComparing;
if (isComparing) {
_calculateCompareDates();
} else {
compareStartDate = null;
compareEndDate = null;
_clearComparisonData();
}
await getAll();
}
void _clearComparisonData() {
chartDataPassengersCompare.clear();
chartDataDriversCompare.clear();
chartDataRidesCompare.clear();
chartDataDriversMatchingNotesCompare.clear();
employeeDataCompare.clear();
}
Map<String, dynamic> _getPayload(DateTime start, DateTime end) => {
"start_date": DateFormat('yyyy-MM-dd').format(start),
"end_date": DateFormat('yyyy-MM-dd').format(end),
"month": start.month.toString(),
"year": start.year.toString(),
};
// ─── Main Fetch ────────────────────────────────────────────
Future<void> getAll() async {
if (startDate == null || endDate == null) return;
isLoading = true;
update();
await Future.wait([
fetchPassengers(isCompare: false),
fetchRides(isCompare: false),
fetchDrivers(isCompare: false),
fetchEmployeeDynamic(isCompare: false),
fetchEditorCallsDynamic(isCompare: false),
fetchEmploymentStats(),
]);
if (isComparing && compareStartDate != null && compareEndDate != null) {
await Future.wait([
fetchPassengers(isCompare: true),
fetchRides(isCompare: true),
fetchDrivers(isCompare: true),
fetchEmployeeDynamic(isCompare: true),
fetchEditorCallsDynamic(isCompare: true),
]);
}
isLoading = false;
update();
}
// ─── Spot Generator ───────────────────────────────────────
List<FlSpot> _generateSpots(
List<dynamic> data,
String dateKey,
String valueKey,
DateTime startOfRange,
DateTime endOfRange,
) {
Map<String, double> dataMap = {
for (var item in data)
item[dateKey].toString():
double.tryParse(item[valueKey].toString()) ?? 0.0
};
int totalDays = endOfRange.difference(startOfRange).inDays + 1;
return List.generate(totalDays, (i) {
final date = startOfRange.add(Duration(days: i));
final key = DateFormat('yyyy-MM-dd').format(date);
return FlSpot((i + 1).toDouble(), dataMap[key] ?? 0.0);
});
}
/// Generates spots map keyed by employee name from a date→name→value structure
Map<String, List<FlSpot>> _generateEmployeeSpots(
Map<String, Map<String, double>> dateNameMap,
DateTime start,
DateTime end,
) {
// Discover all employee names dynamically
final Set<String> names = {};
for (var dayData in dateNameMap.values) {
names.addAll(dayData.keys);
}
int totalDays = end.difference(start).inDays + 1;
final Map<String, List<FlSpot>> result = {};
for (final name in names) {
result[name] = List.generate(totalDays, (i) {
final date = start.add(Duration(days: i));
final dateStr = DateFormat('yyyy-MM-dd').format(date);
final value = dateNameMap[dateStr]?[name] ?? 0.0;
return FlSpot((i + 1).toDouble(), value);
});
}
return result;
}
/// Parses a list of {date/day, NAME, count} records into a dateNameMap
Map<String, Map<String, double>> _parseDateNameMap(List<dynamic> jsonData) {
final Map<String, Map<String, double>> result = {};
for (var item in jsonData) {
final dateStr = (item['date'] ?? item['day']).toString();
final name = item['NAME'].toString().toLowerCase().trim();
final count = double.tryParse(item['count'].toString()) ?? 0.0;
result.putIfAbsent(dateStr, () => {})[name] =
(result[dateStr]?[name] ?? 0) + count;
}
return result;
}
// ─── Passengers ───────────────────────────────────────────
Future<void> fetchPassengers({bool isCompare = false}) async {
final start = isCompare ? compareStartDate! : startDate!;
final end = isCompare ? compareEndDate! : endDate!;
final res = await CRUD().get(
link: AppLink.getPassengersStatic, payload: _getPayload(start, end));
final json = jsonDecode(res);
if (json['status'] == 'failure') return;
final List<dynamic> data = json['message'];
if (!isCompare && data.isNotEmpty && data[0]['totalMonthly'] != null) {
totalMonthlyPassengers = data[0]['totalMonthly'].toString();
}
final spots = _generateSpots(data, 'day', 'totalPassengers', start, end);
if (isCompare)
chartDataPassengersCompare = spots;
else
chartDataPassengers = spots;
}
// ─── Rides ────────────────────────────────────────────────
Future<void> fetchRides({bool isCompare = false}) async {
final start = isCompare ? compareStartDate! : startDate!;
final end = isCompare ? compareEndDate! : endDate!;
final res = await CRUD()
.get(link: AppLink.getRidesStatic, payload: _getPayload(start, end));
final json = jsonDecode(res);
if (json['status'] == 'failure') return;
final List<dynamic> data = json['message'];
if (!isCompare && data.isNotEmpty && data[0]['totalMonthly'] != null) {
totalMonthlyRides = data[0]['totalMonthly'].toString();
}
final spots = _generateSpots(data, 'day', 'totalRides', start, end);
if (isCompare)
chartDataRidesCompare = spots;
else
chartDataRides = spots;
}
// ─── Drivers ──────────────────────────────────────────────
Future<void> fetchDrivers({bool isCompare = false}) async {
final start = isCompare ? compareStartDate! : startDate!;
final end = isCompare ? compareEndDate! : endDate!;
final res = await CRUD().get(
link: AppLink.getdriverstotalMonthly, payload: _getPayload(start, end));
final json = jsonDecode(res);
if (json['status'] == 'failure') return;
final List<dynamic> data = json['message'];
if (!isCompare &&
data.isNotEmpty &&
data[0]['totalMonthlyDrivers'] != null) {
totalMonthlyDrivers = data[0]['totalMonthlyDrivers'].toString();
staticList = data;
}
final spotsDrivers =
_generateSpots(data, 'day', 'dailyTotalDrivers', start, end);
final spotsNotes =
_generateSpots(data, 'day', 'dailyMatchingNotes', start, end);
if (isCompare) {
chartDataDriversCompare = spotsDrivers;
chartDataDriversMatchingNotesCompare = spotsNotes;
} else {
chartDataDrivers = spotsDrivers;
chartDataDriversMatchingNotes = spotsNotes;
}
}
// ─── 🔥 DYNAMIC: Employee Notes ───────────────────────────
Future<void> fetchEmployeeDynamic({bool isCompare = false}) async {
try {
final start = isCompare ? compareStartDate! : startDate!;
final end = isCompare ? compareEndDate! : endDate!;
final res = await CRUD().get(
link: AppLink.getEmployeeStatic, payload: _getPayload(start, end));
if (res == 'failure') return;
final json = jsonDecode(res) as Map<String, dynamic>;
if (json['status'] == 'failure') return;
final List<dynamic> data = json['message'];
if (data.isEmpty) return;
final dateNameMap = _parseDateNameMap(data);
final spotsMap = _generateEmployeeSpots(dateNameMap, start, end);
// Merge into employee data map
final target = isCompare ? employeeDataCompare : employeeData;
spotsMap.forEach((name, spots) {
final color = _getOrAssignColor(name);
if (target.containsKey(name)) {
target[name] = target[name]!.copyWith(notesSpots: spots);
} else {
target[name] = EmployeeChartData(
name: name,
color: color,
notesSpots: spots,
callsSpots: [],
);
}
});
} catch (e) {
Log.print('Error in fetchEmployeeDynamic: $e');
}
}
// ─── 🔥 DYNAMIC: Employee Calls ───────────────────────────
Future<void> fetchEditorCallsDynamic({bool isCompare = false}) async {
try {
final start = isCompare ? compareStartDate! : startDate!;
final end = isCompare ? compareEndDate! : endDate!;
final res = await CRUD().get(
link: AppLink.getEditorStatsCalls, payload: _getPayload(start, end));
if (res == 'failure') return;
final json = jsonDecode(res) as Map<String, dynamic>;
if (json['status'] == 'failure') return;
final List<dynamic> data = json['message'];
if (data.isEmpty) return;
final dateNameMap = _parseDateNameMap(data);
final spotsMap = _generateEmployeeSpots(dateNameMap, start, end);
final target = isCompare ? employeeDataCompare : employeeData;
spotsMap.forEach((name, spots) {
final color = _getOrAssignColor(name);
if (target.containsKey(name)) {
target[name] = target[name]!.copyWith(callsSpots: spots);
} else {
target[name] = EmployeeChartData(
name: name,
color: color,
notesSpots: [],
callsSpots: spots,
);
}
});
} catch (e) {
Log.print('Error in fetchEditorCallsDynamic: $e');
}
}
// ─── Employment Stats ─────────────────────────────────────
Future<void> fetchEmploymentStats() async {
try {
final res = await CRUD().get(
link: AppLink.getEmployeeDriverAfterCallingRegister,
payload: _getPayload(startDate!, endDate!));
if (res == 'failure') return;
final json = jsonDecode(res);
if (json['status'] != 'success') return;
final List<dynamic> data = json['message']?['data'] ?? [];
// Aggregate by name (dynamic — no hardcoded allowed list)
final Map<String, int> aggregated = {};
for (var item in data) {
final name = item['employmentType'].toString().toLowerCase().trim();
final count = int.tryParse(item['count'].toString()) ?? 0;
aggregated[name] = (aggregated[name] ?? 0) + count;
}
employmentStatsList = aggregated.entries.map((e) {
return EmploymentStat(
name: e.key,
count: e.value,
color: _getOrAssignColor(e.key),
);
}).toList()
..sort((a, b) => b.count.compareTo(a.count)); // sort descending
} catch (e) {
Log.print("Error fetchEmploymentStats: $e");
}
}
// ─── Daily Notes ──────────────────────────────────────────
Future<void> fetchDailyNotes(DateTime date) async {
try {
isLoadingNotes = true;
dailyNotesList.clear();
update();
final res = await CRUD().post(
link: AppLink.getNotesForEmployee,
payload: {"date": DateFormat('yyyy-MM-dd').format(date)});
if (res != 'failure') {
final json = res;
if (json['status'] == 'success') {
dailyNotesList = json['message'];
}
}
} catch (e) {
Log.print("Error fetchDailyNotes: $e");
} finally {
isLoadingNotes = false;
update();
}
}
// ─── Computed Summaries for UI ────────────────────────────
/// Returns sorted list of employees by total notes descending
List<EmployeeChartData> get employeesSortedByNotes {
final list = employeeData.values.toList();
list.sort((a, b) => b.totalNotes.compareTo(a.totalNotes));
return list;
}
/// Returns sorted list of employees by total calls descending
List<EmployeeChartData> get employeesSortedByCalls {
final list = employeeData.values.toList();
list.sort((a, b) => b.totalCalls.compareTo(a.totalCalls));
return list;
}
/// Grand total notes across all employees
int get grandTotalNotes =>
employeeData.values.fold(0, (s, e) => s + e.totalNotes);
/// Grand total calls across all employees
int get grandTotalCalls =>
employeeData.values.fold(0, (s, e) => s + e.totalCalls);
}

View File

@@ -0,0 +1,195 @@
import 'dart:async';
import 'dart:convert';
import 'package:get/get.dart';
import 'package:http/http.dart' as http;
import '../../constant/api_key.dart';
import '../../constant/box_name.dart';
import '../../constant/links.dart';
import '../../main.dart';
import '../../print.dart';
import '../functions/crud.dart';
class WalletAdminController extends GetxController {
bool isLoading = false;
late Map<String, dynamic> jsonResponse;
List<dynamic> walletDetails = [];
List driversWalletPoints = [];
@override
void onInit() {
getWalletForEachDriverToPay();
super.onInit();
}
Future getWalletAdminDash() async {
isLoading = true;
update();
var res = await CRUD().get(link: AppLink.getRidesPerMonth, payload: {});
jsonResponse = jsonDecode(res);
}
Future payToBankDriverAll() async {
for (var i = 0; i < driversWalletPoints.length; i++) {
String token = await getToken();
await Future.delayed(const Duration(seconds: 1));
try {
await payToDriverBankAccount(
token,
driversWalletPoints[i]['total_amount'].toString(),
driversWalletPoints[i]['accountBank'].toString(),
driversWalletPoints[i]['bankCode'].toString(),
driversWalletPoints[i]['name_arabic'].toString(),
driversWalletPoints[i]['driverID'].toString(),
driversWalletPoints[i]['phone'].toString(),
driversWalletPoints[i]['email'].toString(),
);
await Future.delayed(const Duration(seconds: 3));
} on FormatException catch (e) {
// Handle the error or rethrow the exception as needed
}
}
}
Future<void> payToDriverBankAccount(
String token,
String amount,
String bankCardNumber,
String bankCode,
String name,
String driverId,
String phone,
String email) async {
var headers = {
'Authorization': 'Bearer $token',
'Content-Type': 'application/json',
};
var body = jsonEncode({
"issuer": "bank_card",
"amount": amount,
"full_name": name,
"bank_card_number": bankCardNumber,
"bank_code": bankCode,
"bank_transaction_type": "cash_transfer"
});
var response = await http.post(
Uri.parse(
'https://stagingpayouts.paymobsolutions.com/api/secure/disburse/'),
headers: headers,
body: body);
if (response.statusCode == 200) {
var d = jsonDecode(response.body);
if (d['status_description'] ==
"Transaction received and validated successfully. Dispatched for being processed by the bank") {
await addPayment('payFromSeferToDriver', driverId,
((-1) * double.parse(amount)).toString());
await addSeferWallet('payFromSeferToDriver', driverId,
((-1) * double.parse(amount)).toString());
await updatePaymentToPaid(driverId);
await sendEmail(driverId, amount, phone, name, bankCardNumber, email);
}
} else {}
}
// String paymentToken = '';
Future<String> generateToken(String amount) async {
var res = await CRUD().post(link: AppLink.addPaymentTokenDriver, payload: {
'driverID': box.read(BoxName.driverID).toString(),
'amount': amount.toString(),
});
var d = jsonDecode(res);
return d['message'];
}
Future sendEmail(
String driverId, amount, phone, name, bankCardNumber, email) async {
await CRUD().sendEmail(AppLink.sendEmailToDrivertransaction, {
"driverID": driverId,
"total_amount": amount,
"phone": phone,
"name_arabic": name,
"accountBank": bankCardNumber,
"email": email
});
}
Future addSeferWallet(
String paymentMethod, String driverID, String point) async {
var seferToken = await generateToken(point.toString());
await CRUD().post(link: AppLink.addSeferWallet, payload: {
'amount': point.toString(),
'paymentMethod': paymentMethod,
'passengerId': 'driver',
'token': seferToken,
'driverId': driverID.toString(),
});
}
Future addPayment(
String paymentMethod, String driverID, String amount) async {
var paymentToken =
await generateToken(((double.parse(amount))).toStringAsFixed(0));
await CRUD().post(link: AppLink.addDrivePayment, payload: {
'rideId': DateTime.now().toIso8601String(),
'amount': ((double.parse(amount))).toStringAsFixed(0),
'payment_method': paymentMethod,
'passengerID': 'myself',
'token': paymentToken,
'driverID': driverID.toString(),
});
}
Future updatePaymentToPaid(String driverID) async {
await CRUD().post(link: AppLink.updatePaymetToPaid, payload: {
'driverID': driverID.toString(),
});
}
Future<String> getToken() async {
var headers = {
'Content-Type': 'application/x-www-form-urlencoded',
// 'Cookie':
// 'csrftoken=74iZJ8XYyuTm5WRq2W4tpWX5eqoJLZVK5QhuDrChWpDtzpgGA269bbCWuEcW85t4'
};
var body = {
'grant_type': 'password',
'username': AK.payMobOutUserName,
'password': AK.payMobOutPassword,
'client_id': AK.payMobOutClient_id,
'client_secret': AK.payMobOutClientSecrret
};
var res = await http.post(
Uri.parse(
'https://stagingpayouts.paymobsolutions.com/api/secure/o/token/'),
headers: headers,
body: body,
);
String token = '';
if (res.statusCode == 200) {
var decode = jsonDecode(res.body);
token = decode['access_token'];
}
return token;
}
Future getWalletForEachDriverToPay() async {
isLoading = true;
update();
var res = await CRUD()
.postWallet(link: AppLink.getVisaForEachDriver, payload: {});
var d = (res);
if (d != 'failure') {
driversWalletPoints = d['message'];
isLoading = false;
update();
}
driversWalletPoints = [];
isLoading = false;
update();
}
}

View File

@@ -0,0 +1,56 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'otp_helper.dart';
class OtpVerificationAdmin extends StatefulWidget {
final String phone;
const OtpVerificationAdmin({required this.phone});
@override
State<OtpVerificationAdmin> createState() => _OtpVerificationAdminState();
}
class _OtpVerificationAdminState extends State<OtpVerificationAdmin> {
final _otpController = TextEditingController();
bool _isLoading = false;
Future<void> _verifyOtp() async {
setState(() => _isLoading = true);
final otpHelper = OtpHelper();
await otpHelper.verifyOtp(widget.phone, _otpController.text.trim());
// if (success) {
// Get.offAllNamed('/admin-dashboard');
// } else {
// Get.snackbar('خطأ', 'رمز التحقق غير صحيح');
// }
setState(() => _isLoading = false);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('التحقق من الرمز')),
body: Padding(
padding: const EdgeInsets.all(20.0),
child: Column(
children: [
TextFormField(
controller: _otpController,
keyboardType: TextInputType.number,
decoration: const InputDecoration(labelText: 'رمز التحقق'),
),
const SizedBox(height: 20),
_isLoading
? const CircularProgressIndicator()
: ElevatedButton(
onPressed: _verifyOtp,
child: const Text('تحقق وأدخل'),
)
],
),
),
);
}
}

View File

@@ -0,0 +1,296 @@
import 'package:flutter/material.dart';
import 'package:jwt_decoder/jwt_decoder.dart';
import 'package:get/get.dart';
import 'package:siro_admin/controller/functions/device_info.dart';
import 'package:siro_admin/views/auth/login_page.dart';
import '../../constant/box_name.dart';
import '../../constant/links.dart';
import '../../constant/info.dart';
import '../../main.dart';
import '../../print.dart';
import '../../views/admin/admin_home_page.dart';
import '../../views/widgets/snackbar.dart';
import '../admin/dashboard_controller.dart';
import '../functions/crud.dart';
import '../functions/encrypt_decrypt.dart';
class OtpHelper extends GetxController {
static final String _sendOtpUrl =
'${AppLink.server}/Admin/auth/send_otp_admin.php';
static final String _verifyOtpUrl =
'${AppLink.server}/Admin/auth/verify_otp_admin.php';
static final String _checkAdminLogin =
'${AppLink.server}/Admin/auth/login.php';
/// إرسال OTP
static Future<bool> sendOtp(String phoneNumber) async {
try {
// await CRUD().getJWT();
final response = await CRUD().post(
link: _sendOtpUrl,
payload: {'receiver': phoneNumber},
);
// Log.print('_sendOtpUrl: ${_sendOtpUrl}');
// Log.print('response: ${response}');
if (response != 'failure') {
mySnackbarSuccess('تم إرسال رمز التحقق إلى رقمك عبر WhatsApp');
return true;
} else {
mySnackeBarError('حدث خطأ من الخادم. حاول مجددًا.');
return false;
}
} catch (e) {
Log.print('OTP SEND ERROR: $e');
mySnackeBarError('حدث خطأ أثناء الإرسال: $e');
return false;
}
}
/// التحقق من OTP
Future<void> verifyOtp(String phoneNumber, String otp) async {
try {
final response = await CRUD().post(
link: _verifyOtpUrl,
payload: {
'phone_number': phoneNumber,
'otp': otp,
'device_number': box.read(BoxName.fingerPrint)
},
);
if (response != 'failure') {
if (response['status'] == 'success') {
box.write(BoxName.phoneVerified, true);
box.write(BoxName.adminPhone, phoneNumber);
mySnackbarSuccess('تم التحقق من الرقم بنجاح');
await checkAdminLogin();
} else {
mySnackeBarError(response['message'] ?? 'فشل في التحقق.');
}
} else {
mySnackeBarError('فشل من الخادم. حاول مرة أخرى.');
}
} catch (e) {
Log.print('OTP VERIFY ERROR: $e');
mySnackeBarError('خطأ في التحقق: $e');
}
}
/// تسجيل الدخول بكلمة المرور والبصمة
Future<bool> loginWithPassword(String password) async {
try {
final fingerprint = box.read(BoxName.fingerPrint);
final response = await CRUD().post(
link: _checkAdminLogin,
payload: {
'fingerprint': fingerprint,
'password': password,
},
);
if (response != 'failure') {
// إذا كان الرد يتطلب OTP (السيرفر يرجعها بداخل message)
final msg = response['message'];
if (response['status'] == 'otp_required' ||
(msg is Map && msg['status'] == 'otp_required')) {
String phone = (msg is Map ? msg['phone'] : response['phone']) ?? '';
_showOtpDialog(phone, password, fingerprint);
return false; // ننتظر إكمال الـ OTP
}
// إذا نجح الدخول مباشرة
return _handleLoginSuccess(response, password);
} else {
// سيقوم CRUD بإظهار الخطأ المناسب (مثل "حسابك قيد المراجعة")
return false;
}
} catch (e) {
Log.print('LOGIN ERROR: $e');
mySnackeBarError('حدث خطأ أثناء تسجيل الدخول: $e');
return false;
}
}
/// التحقق من OTP الخاص بتسجيل الدخول
Future<void> verifyLoginOtp(
String phone, String otp, String password, String fingerprint) async {
try {
final response = await CRUD().post(
link: '${AppLink.server}/Admin/auth/verify_login.php',
payload: {
'phone': phone,
'otp': otp,
'fingerprint': fingerprint,
},
);
if (response != 'failure') {
bool success = await _handleLoginSuccess(response, password);
if (success) {
try {
if (Get.isRegistered<DashboardController>()) {
Get.find<DashboardController>().getDashBoard();
}
} catch (e) {}
Get.offAll(() => const AdminHomePage());
}
}
} catch (e) {
Log.print('OTP VERIFY LOGIN ERROR: $e');
mySnackeBarError('خطأ في التحقق من الرمز: $e');
}
}
Future<bool> _handleLoginSuccess(dynamic response, String password) async {
final msg = response['message'];
final data = response['admin'] ?? (msg is Map ? msg['admin'] : null);
final jwt = response['jwt'] ?? (msg is Map ? msg['jwt'] : null);
if (jwt != null) {
await box.write(BoxName.jwt, c(jwt));
}
if (data != null) {
if (data['id'] != null) await box.write(BoxName.driverID, data['id']);
if (data['role'] != null) {
String role = data['role'].toString().trim();
await box.write('admin_role', role);
Log.print('Admin role saved: $role');
}
if (data['phone'] != null)
await box.write(BoxName.adminPhone, data['phone']);
}
await box.write(BoxName.phoneVerified, true);
await box.write('admin_password', password);
mySnackbarSuccess('تم تسجيل الدخول بنجاح');
return true;
}
void _showOtpDialog(String phone, String password, String fingerprint) {
String otpCode = '';
Get.defaultDialog(
title: 'رمز التحقق',
content: Column(
children: [
Text('تم إرسال رمز التحقق إلى WhatsApp الخاص بك ($phone)'),
const SizedBox(height: 16),
TextField(
onChanged: (val) => otpCode = val,
keyboardType: TextInputType.number,
decoration: const InputDecoration(
hintText: 'أدخل الرمز هنا',
border: OutlineInputBorder(),
),
),
],
),
textConfirm: 'تحقق',
confirmTextColor: Colors.white,
onConfirm: () {
if (otpCode.length >= 3) {
Get.back();
verifyLoginOtp(phone, otpCode, password, fingerprint);
} else {
mySnackeBarError('الرجاء إدخال رمز صحيح');
}
},
);
}
static bool _isAutoLoginAttempted = false;
Future<void> checkAdminLogin() async {
final fingerprint = box.read(BoxName.fingerPrint);
final password = box.read('admin_password');
if (fingerprint == null || password == null) {
Get.offAll(() => const AdminLoginPage());
return;
}
// ─── أولاً: التحقق من وجود توكن JWT صالح ─────────
// إذا وُجد توكن غير منتهي الصلاحية → ندخل مباشرة بدون استدعاء login.php
final rawJwt = box.read(BoxName.jwt);
if (rawJwt != null) {
try {
String token = r(rawJwt.toString()).split(AppInformation.addd)[0];
if (!JwtDecoder.isExpired(token)) {
Log.print('Valid JWT found, skipping login.php (no OTP needed)');
Get.offAll(() => const AdminHomePage());
return;
}
Log.print('JWT expired, need fresh login');
} catch (e) {
Log.print('JWT decode failed: \$e, need fresh login');
}
}
// ─── ثانياً: لا يوجد توكن صالح → استدعاء login.php ───
final response = await CRUD().post(
link: _checkAdminLogin,
payload: {
'fingerprint': fingerprint,
'password': password,
'is_renewal': '1',
},
);
if (response != 'failure') {
final msg = response['message'];
if (response['status'] == 'otp_required' ||
(msg is Map && msg['status'] == 'otp_required')) {
String phone = (msg is Map ? msg['phone'] : response['phone']) ?? '';
_showOtpDialog(phone, password, fingerprint);
return; // ننتظر إدخال رمز التحقق
}
if (msg is Map && msg['jwt'] != null) {
box.write(BoxName.jwt, c(msg['jwt']));
if (msg['admin'] != null && msg['admin']['id'] != null) {
box.write(BoxName.driverID, msg['admin']['id']);
}
} else if (response['jwt'] != null) {
box.write(BoxName.jwt, c(response['jwt']));
}
try {
if (Get.isRegistered<DashboardController>()) {
Get.find<DashboardController>().getDashBoard();
}
} catch (e) {}
Get.offAll(() => const AdminHomePage());
} else {
Log.print('Auto-login failed, redirecting to login page');
Get.offAll(() => const AdminLoginPage());
}
}
@override
void onInit() {
super.onInit();
DeviceHelper.getDeviceFingerprint().then((deviceFingerprint) {
box.write(BoxName.fingerPrint, deviceFingerprint);
});
// تأجيل تنفيذ التنقل حتى تنتهي مرحلة البناء
// ⚠️ محاولة واحدة فقط للتسجيل التلقائي لمنع الحلقة اللانهائية
Future.microtask(() {
if (_isAutoLoginAttempted) {
// نحن في حلقة — نتوقف ونذهب لصفحة الدخول مباشرة
Log.print('Auto-login already attempted, skipping to login page');
return;
}
_isAutoLoginAttempted = true;
if (box.read(BoxName.phoneVerified) == true &&
box.read(BoxName.adminPhone) != null) {
checkAdminLogin();
} else {
Get.offAll(() => AdminLoginPage());
}
});
}
}

View File

@@ -0,0 +1,113 @@
import 'dart:convert';
import 'package:get/get.dart';
import 'package:http/http.dart' as http;
import '../../../constant/links.dart';
import '../../constant/api_key.dart';
import '../../constant/box_name.dart';
import '../../constant/colors.dart';
import '../../main.dart';
import '../functions/crud.dart';
class PaymobPayout extends GetxController {
bool isLoading = false;
String dropdownValue = 'etisalat';
Future<String> getToken() async {
var headers = {
'Content-Type': 'application/x-www-form-urlencoded',
// 'Cookie':
// 'csrftoken=74iZJ8XYyuTm5WRq2W4tpWX5eqoJLZVK5QhuDrChWpDtzpgGA269bbCWuEcW85t4'
};
var body = {
'grant_type': 'password',
'username': AK.payMobOutUserName,
'password': AK.payMobOutPassword,
'client_id': AK.payMobOutClient_id,
'client_secret': AK.payMobOutClientSecrret
};
var res = await http.post(
Uri.parse('https://payouts.paymobsolutions.com/api/secure/o/token/'),
headers: headers,
body: body,
);
String token = '';
if (res.statusCode == 200) {
var decode = jsonDecode(res.body);
token = decode['access_token'];
}
return token;
}
payToDriverWallet(
String token, String amount, String issuer, String msisdn) async {
var headers = {
'Authorization': 'Bearer $token',
'Content-Type': 'application/json',
};
var body = json.encode({
"amount": amount, //"10.00",
"issuer": issuer, //"vodafone",
"msisdn": msisdn, // "01023456789"
});
var res = await http.post(
Uri.parse('https://payouts.paymobsolutions.com/api/secure/disburse/'),
headers: headers,
body: body,
);
var dec = jsonDecode(res.body);
if (dec['disbursement_status'] == 'successful') {
await CRUD().post(link: AppLink.addDriverpayment, payload: {
'rideId': DateTime.now().toIso8601String(),
'amount': ((-1) * (double.parse(dec['amount'])) + 5).toStringAsFixed(0),
'payment_method': 'payout',
'passengerID': 'admin',
'driverID': box.read(BoxName.driverID).toString(),
});
Get.snackbar('Transaction successful'.tr,
'${'Transaction successful'.tr} ${dec['amount']}',
backgroundColor: AppColor.greenColor);
// Get.find<CaptainWalletController>().getCaptainWalletFromRide();
} else if (dec['disbursement_status'] == 'failed') {
Get.snackbar('Transaction failed'.tr, 'Transaction failed'.tr,
backgroundColor: AppColor.redColor);
}
}
payToDriverBankAccount(String token, String amount, String bankCardNumber,
String bankCode) async {
var headers = {
'Authorization': 'Bearer $token',
'Content-Type': 'application/json',
};
var body = {
"issuer": "bank_card",
"amount": amount, //9.0,
"full_name":
'${box.read(BoxName.nameDriver)} ${box.read(BoxName.lastNameDriver)}',
"bank_card_number": bankCardNumber, //"1111-2222-3333-4444",
"bank_code": bankCode, //"CIB",
"bank_transaction_type": "cash_transfer"
};
var res = await http
.post(
Uri.parse('https://payouts.paymobsolutions.com/api/secure/disburse/'),
headers: headers,
body: body,
)
.then((value) {});
}
Future payToWalletDriverAll(
String amount, String issuer, String msisdn) async {
String token = await getToken();
await payToDriverWallet(token, amount, issuer, msisdn);
}
Future payToBankDriverAll(
String amount, String bankCardNumber, String bankCode) async {
String token = await getToken();
await payToDriverBankAccount(token, amount, bankCardNumber, bankCode);
}
}

View File

@@ -0,0 +1,37 @@
import 'package:get/get.dart';
import '../../constant/links.dart';
import '../functions/crud.dart';
class DriverController extends GetxController {
List drivers = [];
Map driverDetails = {};
// جلب السائقين pending
getDriversPending() async {
var res = await CRUD().post(
link: AppLink.getDriversPending, // رابط drivers_pending_list.php
payload: {},
);
if (res != 'failure') {
drivers = (res)['message'];
update(['drivers']); // تحديث الـ UI
} else {
Get.snackbar('Error', 'Failed to load drivers');
}
}
// جلب تفاصيل سائق واحد
getDriverDetails(String driverId) async {
var res = await CRUD().post(
link: AppLink.getDriverDetails, // رابط driver_details.php
payload: {"id": driverId},
);
if (res != 'failure') {
driverDetails = (res)['message'];
update(['driverDetails']); // تحديث صفحة التفاصيل
} else {
Get.snackbar('Error', 'Failed to load driver details');
}
}
}

View File

@@ -0,0 +1,68 @@
import 'dart:convert';
import 'package:get/get.dart';
import '../../constant/colors.dart';
import '../../constant/links.dart';
import '../functions/crud.dart';
class Driverthebest extends GetxController {
bool isLoading = false;
List driver = [];
getBestDriver() async {
var res = await CRUD().get(link: AppLink.getBestDriver, payload: {});
if (res != 'failure') {
driver = jsonDecode(res)['message'];
update();
} else {
Get.snackbar('error', '', backgroundColor: AppColor.redColor);
}
}
@override
void onInit() {
getBestDriver();
super.onInit();
}
}
class DriverTheBestGizaController extends GetxController {
bool isLoading = false;
List driver = [];
getBestDriver() async {
var res = await CRUD().get(link: AppLink.getBestDriverGiza, payload: {});
if (res != 'failure') {
driver = jsonDecode(res)['message'];
update();
} else {
Get.snackbar('error', '', backgroundColor: AppColor.redColor);
}
}
@override
void onInit() {
getBestDriver();
super.onInit();
}
}
class DriverTheBestAlexandriaController extends GetxController {
bool isLoading = false;
List driver = [];
getBestDriver() async {
var res =
await CRUD().get(link: AppLink.getBestDriverAlexandria, payload: {});
if (res != 'failure') {
driver = jsonDecode(res)['message'];
update();
} else {
Get.snackbar('error', '', backgroundColor: AppColor.redColor);
}
}
@override
void onInit() {
getBestDriver();
super.onInit();
}
}

View File

@@ -0,0 +1,82 @@
import 'dart:convert';
import 'dart:math';
import 'package:flutter/widgets.dart';
import 'package:get/get.dart';
import 'package:siro_admin/constant/colors.dart';
import 'package:siro_admin/constant/links.dart';
import 'package:siro_admin/controller/functions/crud.dart';
class EmployeeController extends GetxController {
List employee = [];
final name = TextEditingController();
final education = TextEditingController();
final site = TextEditingController();
final phone = TextEditingController();
final status = TextEditingController();
final formKey = GlobalKey<FormState>();
fetchEmployee() async {
var res = await CRUD().get(link: AppLink.getEmployee, payload: {});
if (res is String && (res == 'failure' || res == 'token_expired')) {
Get.snackbar('error', 'Failed to load employees', backgroundColor: AppColor.redColor);
return;
}
try {
var jsonData = res is String ? jsonDecode(res) : res;
employee = jsonData['message'];
update();
} catch (e) {
Get.snackbar('error', 'Invalid server response', backgroundColor: AppColor.redColor);
}
}
late String id;
String generateRandomId(int length) {
const String chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
Random random = Random();
return String.fromCharCodes(Iterable.generate(
length,
(_) => chars.codeUnitAt(random.nextInt(chars.length)),
));
}
addEmployee() async {
// Create the payload with the employee data
var res = await CRUD().post(link: AppLink.addEmployee, payload: {
"id": id,
"name": name.text,
"education": education.text,
"site": site.text,
"phone": phone.text,
"status": status.text,
});
// Check the response from the API
if (res != 'failure') {
// You can handle the success case here, such as showing a success message
Get.back();
Get.snackbar('Success', 'Employee added successfully',
backgroundColor: AppColor.greenColor);
name.clear();
education.clear();
site.clear();
phone.clear();
status.clear();
fetchEmployee();
} else {
// Handle the error case by showing a snackbar with an error message
Get.snackbar('Error', 'Failed to add employee',
backgroundColor: AppColor.redColor);
}
}
@override
void onInit() {
fetchEmployee();
super.onInit();
}
}

View File

@@ -0,0 +1,517 @@
import 'dart:convert';
import 'dart:io';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:http/http.dart' as http;
import 'package:secure_string_operations/secure_string_operations.dart';
import 'package:siro_admin/constant/info.dart';
import 'package:siro_admin/env/env.dart';
import '../../constant/api_key.dart';
import '../../constant/box_name.dart';
import '../../constant/char_map.dart';
import '../../constant/colors.dart';
import '../../constant/links.dart';
import '../../constant/style.dart';
import '../../main.dart';
import '../../print.dart';
import '../../views/widgets/elevated_btn.dart';
import '../functions/encrypt_decrypt.dart';
import '../notification_controller.dart';
import 'local_notification.dart';
import 'token_access.dart';
class FirebaseMessagesController extends GetxController {
final fcmToken = FirebaseMessaging.instance;
List<String> tokens = [];
List<String> tokensPassengers = [];
List dataTokens = [];
List dataTokensPassenger = [];
late String driverID;
late String driverToken;
NotificationSettings? notificationSettings;
bool isLoading = false;
Future<void> getNotificationSettings() async {
// Get the current notification settings
NotificationSettings? notificationSettings =
await FirebaseMessaging.instance.getNotificationSettings();
'Notification authorization status: ${notificationSettings.authorizationStatus}';
// Call the update function if needed
update();
}
Future<void> requestFirebaseMessagingPermission() async {
FirebaseMessaging messaging = FirebaseMessaging.instance;
// Check if the platform is Android
if (Platform.isAndroid) {
// Request permission for Android
await messaging.requestPermission();
} else if (Platform.isIOS) {
// Request permission for iOS
NotificationSettings settings = await messaging.requestPermission(
alert: true,
announcement: true,
badge: true,
carPlay: true,
criticalAlert: true,
provisional: false,
sound: true,
);
messaging.setForegroundNotificationPresentationOptions(
alert: true, badge: true, sound: true);
}
}
Future getTokens() async {
var res = await http.post(
Uri.parse(AppLink.getTokens),
headers: {
'Authorization':
'Basic ${base64Encode(utf8.encode(AK.basicAuthCredentials))}',
},
body: {},
);
var jsonResponse = jsonDecode(res.body);
if (jsonResponse['status'] == 'success') {
dataTokens = jsonResponse['data'];
for (var i = 0; i < dataTokens.length; i++) {
tokens.add(jsonResponse['data'][i]['token']);
}
box.write(BoxName.tokens, tokens);
} else {
Get.defaultDialog(title: "Warning", middleText: "Server Error");
}
}
var currentPage = 1;
var totalPages = 1;
Future<void> getAllTokenDrivers() async {
isLoading = true;
try {
var res = await http.post(
Uri.parse(AppLink.getDriversPhonesAndTokens),
headers: {
"Content-Type": "application/x-www-form-urlencoded",
'Authorization':
'Bearer ${X.r(X.r(X.r(box.read(BoxName.jwt), cn), cC), cs).toString().split(AppInformation.addd)[0]}'
// 'Authorization': 'Bearer ${box.read(BoxName.jwt)}'
},
body: {
// 'page': page.toString(),
},
);
var jsonResponse = jsonDecode(res.body);
Log.print('jsonResponse: ${jsonResponse}');
if (jsonResponse['status'] == 'success') {
// var newData = jsonResponse['data'] as List;
// Log.print('newData: ${newData}');
// // if (page == 1) {
// // dataTokens.clear();
// // tokens.clear();
// // }
// dataTokens.addAll(newData);
// for (var item in newData) {
// tokens.add(item['token']);
// }
// currentPage = int.parse(jsonResponse['currentPage']);
// totalPages = jsonResponse['totalPages'];
box.write(BoxName.tokensDrivers, jsonResponse);
Log.print(
'box.write(BoxName.tokensDrivers: ${box.read(BoxName.tokensDrivers)}');
} else {
Get.defaultDialog(
title: "Warning", middleText: "No more data available");
}
} catch (e) {
Get.defaultDialog(title: "Error", middleText: "Server Error: $e");
} finally {
isLoading = false;
}
}
var currentPagePassenger = 1;
var totalPagesPassenger = 1;
Future<void> getAllTokenPassenger({int page = 1}) async {
isLoading = true;
try {
var res = await http.post(
Uri.parse(AppLink.getAllTokenPassengers),
headers: {
"Content-Type": "application/x-www-form-urlencoded",
'Authorization':
'Bearer ${X.r(X.r(X.r(box.read(BoxName.jwt), cn), cC), cs).toString().split(AppInformation.addd)[0]}'
},
body: {},
);
var jsonResponse = jsonDecode(res.body);
if (jsonResponse['status'] == 'success') {
box.write(BoxName.tokensPassengers, jsonResponse);
Log.print(
'box.write(BoxName.tokensPassenger: ${box.read(BoxName.tokensPassengers)}');
} else {
Get.defaultDialog(
title: "Warning", middleText: "No more data available");
}
} catch (e) {
Get.defaultDialog(title: "Error", middleText: "Server Error: $e");
} finally {
isLoading = false;
}
}
bool isSendingNotifications = false;
Future<void> loadAllPagesAndSendNotifications() async {
isSendingNotifications = true;
// currentPage = 1;
// while (currentPage <= totalPages) {
// await getAllTokenDrivers(page: currentPage);
await getAllTokenDrivers();
// Log.print('tokens: ${tokens}');
await NotificationController().sendNotificationDrivers();
// print(tokens);
// if (currentPage < totalPages) {
// await Future.delayed(const Duration(seconds: 3));
// }
// currentPage++;
// }
isSendingNotifications = false;
Get.snackbar("Success", "All notifications sent!");
}
bool isSendingNotificationsPassenger = false;
Future<void> loadAllPagesAndSendNotificationsPassengers() async {
isSendingNotificationsPassenger = true;
currentPage = 1;
// while (currentPagePassenger <= totalPagesPassenger) {
await getAllTokenPassenger();
await NotificationController().sendNotificationPassengers();
// print(tokensPassengers);
// if (currentPagePassenger < totalPagesPassenger) {
// await Future.delayed(const Duration(seconds: 3));
// }
// currentPagePassenger++;
// }
isSendingNotificationsPassenger = false;
Get.snackbar("Success", "All notifications sent!");
}
Future getAllTokenPassengers() async {
var res = await http.post(
Uri.parse(AppLink.getAllTokenPassengers),
headers: {
"Content-Type": "application/x-www-form-urlencoded",
'Authorization':
'Bearer ${X.r(X.r(X.r(box.read(BoxName.jwt), cn), cC), cs).toString().split(AppInformation.addd)[0]}'
// 'Authorization': 'Bearer ${box.read(BoxName.jwt)}'
},
body: {},
);
var jsonResponse = jsonDecode(res.body);
if (jsonResponse['status'] == 'success') {
dataTokens = jsonResponse['data'];
for (var i = 0; i < dataTokens.length; i++) {
tokensPassengers.add(jsonResponse['data'][i]['token']);
}
box.write(BoxName.tokensPassengers, jsonResponse['data']);
} else {
Get.defaultDialog(title: "Warning", middleText: "Server Error");
}
}
Future getToken() async {
fcmToken.getToken().then((token) {
if (box.read(BoxName.email) == null) {
box.write(BoxName.tokenDriver, token);
} else {
box.write(BoxName.tokenFCM, token);
}
});
FirebaseMessaging.onMessage.listen((RemoteMessage message) {
// If the app is in the background or terminated, show a system tray message
RemoteNotification? notification = message.notification;
AndroidNotification? android = notification?.android;
// if (notification != null && android != null) {
if (message.data.isNotEmpty && message.notification != null) {
fireBaseTitles(message);
}
});
}
void fireBaseTitles(RemoteMessage message) {
if (message.notification!.title! == 'Order') {
if (Platform.isAndroid) {
// NotificationController().showNotification('Order', '', 'order');
}
var myListString = message.data['DriverList'];
// var points = message.data['PolylineJson'];
var myList = jsonDecode(myListString) as List<dynamic>;
// var myPoints = jsonDecode(points) as List<dynamic>;
driverToken = myList[14].toString();
// This is for location using and uploading status
update();
}
}
SnackbarController driverAppliedTripSnakBar() {
return Get.snackbar(
'Driver Applied the Ride for You'.tr,
'',
colorText: AppColor.greenColor,
duration: const Duration(seconds: 3),
snackPosition: SnackPosition.TOP,
titleText: Text(
'Applied'.tr,
style: const TextStyle(color: AppColor.redColor),
),
messageText: Text(
'Driver Applied the Ride for You'.tr,
style: AppStyle.title,
),
icon: const Icon(Icons.approval),
shouldIconPulse: true,
margin: const EdgeInsets.all(16),
padding: const EdgeInsets.all(16),
);
}
Future<dynamic> passengerDialog(String message) {
return Get.defaultDialog(
barrierDismissible: false,
title: 'message From passenger'.tr,
titleStyle: AppStyle.title,
middleTextStyle: AppStyle.title,
middleText: message.tr,
confirm: MyElevatedButton(
title: 'Ok'.tr,
onPressed: () {
// FirebaseMessagesController().sendNotificationToPassengerToken(
// 'Hi ,I will go now'.tr,
// 'I will go now'.tr,
// Get.find<MapPassengerController>().driverToken, []);
// Get.find<MapPassengerController>()
// .startTimerDriverWaitPassenger5Minute();
Get.back();
}));
}
// Future<dynamic> driverFinishTripDialoge(List<dynamic> driverList) {
// return Get.defaultDialog(
// title: 'Driver Finish Trip'.tr,
// content: const DriverTipWidget(),
// confirm: MyElevatedButton(
// title: 'Yes'.tr,
// onPressed: () async {
// var tip = (Get.find<MapPassengerController>().totalPassenger) *
// (double.parse(box.read(BoxName.tipPercentage.toString())));
// var res = await CRUD().post(link: AppLink.addTips, payload: {
// 'passengerID': box.read(BoxName.passengerID),
// 'driverID': driverList[0].toString(),
// 'rideID': driverList[1].toString(),
// 'tipAmount': tip.toString(),
// });
// await CRUD().post(link: AppLink.addPassengersWallet, payload: {
// 'passenger_id': box.read(BoxName.passengerID).toString(),
// 'balance': ((-1) * tip).toString()
// });
// await CRUD().post(link: AppLink.addDriversWalletPoints, payload: {
// 'driverID': driverList[0].toString(),
// 'paymentID': '${Get.find<MapPassengerController>().rideId}tip',
// 'amount': (tip * 100).toString(),
// 'paymentMethod': 'visa-tip',
// });
// if (res != 'failure') {
// FirebaseMessagesController().sendNotificationToAnyWithoutData(
// 'You Have Tips',
// '${'${tip.toString()}\$${' tips\nTotal is'.tr}'} ${tip + (Get.find<MapPassengerController>().totalPassenger)}',
// driverList[2].toString(),
// );
// }
// Get.to(() => RateDriverFromPassenger(), arguments: {
// 'driverId': driverList[0].toString(),
// 'rideId': driverList[1].toString(),
// 'price': driverList[3].toString()
// });
// },
// kolor: AppColor.greenColor,
// ),
// cancel: MyElevatedButton(
// title: 'No,I want'.tr,
// onPressed: () {
// Get.to(() => RateDriverFromPassenger(), arguments: {
// 'driverId': driverList[0].toString(),
// 'rideId': driverList[1].toString(),
// 'price': driverList[3].toString()
// });
// },
// kolor: AppColor.redColor,
// ));
// }
void sendNotificationAll(String title, body) async {
// Get the token you want to subtract.
String token = box.read(BoxName.tokenFCM);
tokens = box.read(BoxName.tokens);
// Subtract the token from the list of tokens.
tokens.remove(token);
// Save the list of tokens back to the box.
// box.write(BoxName.tokens, tokens);
tokens = box.read(BoxName.tokens);
for (var i = 0; i < tokens.length; i++) {
String serviceAccountKeyJson = '''{
"type": "service_account",
"project_id": "ride-b1bd8",
"private_key_id": "75e817c0b902db2ef35edf2c2bd159dec1f13249",
"private_key": "-----BEGIN PRIVATE KEY-----\\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQD0zH9TQGDQHUv3\\na3/JAD1UKPwAp3wNKT0a6fxiIzjI3JxQWI30QvZCcfl6CdMhIcydX1ncSaYTcEeC\\n/AdPVCPkqyJx1YIGGg6P/mRzCWeaN8fsp6z250m5vcObDCZc3dbJEkepbep+6FPY\\n21m3KO+AHh1glgsTGZOTm5xiU8NGXpdk2QEh8wpiIIlR/HuKwVw9g8urNe3Sno+U\\nDm3z37iFqvZdmpqO8aWTJu6beb3hsREK9XK2I9JqC2JUwiGQRo3idOvPP6hkqrWx\\nKSX96vglQFYfakvJdDp2ZATOlpBYPMtS/IWhJ985u58TSS+Kl8qpnpaZBSxgJirf\\nhWzhnKLfAgMBAAECggEAJP785SePGhS7ZN6ltspm+l+hSjYFrPWFCxq+rlQ1YkHZ\\nC9l+RqKSFhOkiPmQI2s4wbXl3kFxLHHlFNoi/q2wKQBmGb8TQfnRJpjjNHGA61Ev\\n0Ue7/6qPvVb9B2MsLw/FxKiTFPuMG3bgKR9pbSFuJLYoaW7zqITOhVnYphGTqwAY\\nBVVcvISSLvELDmH9VZcv/9DVqVlqbbESHWh1Z4W6XGPoEqeDH/upNTyQQ/46Msgm\\nTGE6VqLHpWuSf6SqHp+r0Y0lI3vIPM1vz5FAJDJbOE/enHa0fSup0OHSMxl0HVMn\\nnO1yrGF3vsIPOej5HKr5d71bEIckzk73/yjNC1/mDQKBgQD7RtUvc9omsSsFMJ6e\\nBASAn6Dktx/QY/XNJjFzHQj69cywLDe5t5AL2gUi3phQ2oqB5XJdwnd5bTIEPEPZ\\nDOuOai2802p6FJk6kjmZAMVGx5JtXBH+vs6jrmQQSMiKbjwN1TT6xIWakvLOonUi\\nX6ZvjYYjU/E0YJU3jSiXWEr76wKBgQD5Zn4SouJ6BCDZMbausJVMBkk3qxsYooip\\np89WakC6e7AZinpkRcqjGGV9GOvc8crJs6fyXAA9ORepGP47Mc0ZrDssOkstznsM\\npr8R0S6MKwEZaT9ixOHdOcLZ47ps+JzA2Wr4KN2OvFHksUkB/46ATD1j9WZVgB8M\\namsYp/Y73QKBgHOo+PvsoZ9psVmkNX6abtAdqdtdB0HOoRea2uwXk0ig12TIFaZg\\nfedWpUKVnxqoXVTJHklV99RmlL0qWDiSH+LfsMnXro0e6iDxqZ1po2Se/CFmXcoa\\nXdctsFVmixhdATuExewfhTfPKABA+xWlXWC/jdy5CK+JPWXijaqMM4edAoGAE5Bj\\nsWiPpYyvWvpYX0nA3G7dzX0hqgQN/mkIjbnWDArp3IcNZNJIvBSM2Yxb7EAXbU0n\\njo6DAkp5Pa2VO+WDNlFZbvW/sf8xjeOCt44WPa6d7nVgIIpbQXRngZoopKW3/jTP\\n/FmQT8McFXmGxZ5belsAsdetSGW9icbLUerTGQ0CgYEAmf/G8Ag3XxmqTXvvHuv2\\n14OP7WnrVqkEMnydrftEwn4peXd/Lz+/GYX5Zc4ZoNgbN8IvZ5z0+OmRsallsbiW\\nBw0/tc68CjzxXOvReWxDluUopqWVGj5tlGqE5xUDku9SWJSxbkiQ3rqutzBdPXpr\\noqHwPyDrmK/Zgqn+uiIm4Ck=\\n-----END PRIVATE KEY-----\\n",
"client_email": "firebase-adminsdk-o2wqi@ride-b1bd8.iam.gserviceaccount.com",
"client_id": "111210077025005706623",
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
"token_uri": "https://oauth2.googleapis.com/token",
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
"client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/firebase-adminsdk-o2wqi%40ride-b1bd8.iam.gserviceaccount.com",
"universe_domain": "googleapis.com"
}
'''; // As defined above
// Initialize AccessTokenManager
final accessTokenManager = AccessTokenManager(serviceAccountKeyJson);
// Obtain an OAuth 2.0 access token
final accessToken = await accessTokenManager.getAccessToken();
// Send the notification
final response = await http
.post(
Uri.parse(
'https://fcm.googleapis.com/v1/projects/ride-b1bd8/messages:send'),
headers: <String, String>{
'Content-Type': 'application/json',
'Authorization': 'Bearer $accessToken',
},
body: jsonEncode({
'notification': <String, dynamic>{
'title': title,
'body': body,
'sound': 'ding.wav'
},
'priority': 'high',
'data': <String, dynamic>{
'click_action': 'FLUTTER_NOTIFICATION_CLICK',
'id': '1',
'status': 'done'
},
'to': tokens[i],
}))
.whenComplete(() {})
.catchError((e) {});
}
}
// for (var i = 0; i < tokens.length; i++) {
// http
// .post(Uri.parse('https://fcm.googleapis.com/fcm/send'),
// headers: <String, String>{
// 'Content-Type': 'application/json',
// 'Authorization': 'key=${storage.read(key: BoxName.serverAPI}'
// },
// body: jsonEncode({
// 'notification': <String, dynamic>{
// 'title': title,
// 'body': body,
// 'sound': 'true'
// },
// 'priority': 'high',
// 'data': <String, dynamic>{
// 'click_action': 'FLUTTER_NOTIFICATION_CLICK',
// 'id': '1',
// 'status': 'done'
// },
// 'to': tokens[i],
// }))
// .whenComplete(() {})
// .catchError((e) {
// });
// }
// }
//android/app/src/main/res/raw/iphone_ringtone.wav
late String serviceAccountKeyJson;
void sendNotificationToAnyWithoutData(
String title, String body, String token, String tone) async {
try {
var encryptedKey = Env.privateKeyFCM;
// Log.print('encryptedKey: ${encryptedKey}');
serviceAccountKeyJson =
EncryptionHelper.instance.decryptData(encryptedKey);
// As defined above
// Initialize AccessTokenManager
final accessTokenManager = AccessTokenManager(serviceAccountKeyJson);
// Obtain an OAuth 2.0 access token
final accessToken = await accessTokenManager.getAccessToken();
// Log.print('accessToken: ${accessToken}');
// Send the notification
final response = await http.post(
Uri.parse(
'https://fcm.googleapis.com/v1/projects/intaleq-d48a7/messages:send'),
headers: <String, String>{
'Content-Type': 'application/json',
'Authorization': 'Bearer $accessToken',
},
body: jsonEncode({
'message': {
'token': token,
'notification': {
'title': title,
'body': body,
},
'android': {
'notification': {
'sound': tone,
},
},
'apns': {
'payload': {
'aps': {
'sound': tone,
},
},
},
},
}),
);
if (response.statusCode == 200) {
SnackBar(content: Text('${response.statusCode}'));
print(
'Notification sent successfully. Status code: ${response.statusCode}');
print('Response body: ${response.body}');
} else {
print(
'Failed to send notification. Status code: ${response.statusCode}');
print('Response body: ${response.body}');
}
} catch (e) {
print('Error sending notification: $e');
}
}
}

View File

@@ -0,0 +1,29 @@
// import 'package:flutter_local_notifications/flutter_local_notifications.dart';
// import 'package:get/get.dart';
// class NotificationController extends GetxController {
// final FlutterLocalNotificationsPlugin _flutterLocalNotificationsPlugin =
// FlutterLocalNotificationsPlugin();
// // Initializes the local notifications plugin
// Future<void> initNotifications() async {
// const AndroidInitializationSettings android =
// AndroidInitializationSettings('@mipmap/launcher_icon');
// const InitializationSettings initializationSettings =
// InitializationSettings(android: android);
// await _flutterLocalNotificationsPlugin.initialize(initializationSettings);
// }
// // Displays a notification with the given title and message
// void showNotification(String title, String message, String tone) async {
// AndroidNotificationDetails android = AndroidNotificationDetails(
// 'your channel id', 'your channel name',
// importance: Importance.max,
// priority: Priority.high,
// showWhen: false,
// sound: RawResourceAndroidNotificationSound(tone));
// NotificationDetails details = NotificationDetails(android: android);
// await _flutterLocalNotificationsPlugin.show(0, title, message, details);
// }
// }

View File

@@ -0,0 +1,59 @@
import 'dart:convert';
import 'package:http/http.dart' as http;
import '../../print.dart';
class NotificationService {
// تأكد من أن هذا هو الرابط الصحيح لملف الإرسال
static const String _serverUrl =
'https://syria.intaleq.xyz/intaleq/fcm/send_fcm.php';
static Future<void> sendNotification({
required String target,
required String title,
required String body,
required String? category, // <-- [الإضافة الأولى]
String? tone,
List<String>? driverList,
bool isTopic = false,
}) async {
try {
final Map<String, dynamic> payload = {
'target': target,
'title': title,
'body': body,
'isTopic': isTopic,
};
if (category != null) {
payload['category'] = category; // <-- [الإضافة الثانية]
}
if (tone != null) {
payload['tone'] = tone;
}
if (driverList != null) {
// [مهم] تطبيق السائق يرسل passengerList
payload['passengerList'] = jsonEncode(driverList);
}
final response = await http.post(
Uri.parse(_serverUrl),
headers: {
'Content-Type': 'application/json; charset=UTF-8',
},
body: jsonEncode(payload),
);
if (response.statusCode == 200) {
print('✅ Notification sent successfully.');
} else {
print(
'❌ Failed to send notification. Status code: ${response.statusCode}');
}
} catch (e) {
print('❌ An error occurred while sending notification: $e');
}
}
}

View File

@@ -0,0 +1,53 @@
import 'dart:convert';
import 'package:googleapis_auth/auth_io.dart';
import '../../print.dart';
class AccessTokenManager {
static final AccessTokenManager _instance = AccessTokenManager._internal();
late final String serviceAccountJsonKey;
AccessToken? _accessToken;
DateTime? _expiryDate;
AccessTokenManager._internal();
factory AccessTokenManager(String jsonKey) {
if (_instance._isServiceAccountKeyInitialized()) {
// Prevent re-initialization
return _instance;
}
_instance.serviceAccountJsonKey = jsonKey;
return _instance;
}
bool _isServiceAccountKeyInitialized() {
try {
serviceAccountJsonKey; // Access to check if initialized
return true;
} catch (e) {
return false;
}
}
Future<String> getAccessToken() async {
if (_accessToken != null && DateTime.now().isBefore(_expiryDate!)) {
return _accessToken!.data;
}
try {
final serviceAccountCredentials = ServiceAccountCredentials.fromJson(
json.decode(serviceAccountJsonKey));
final client = await clientViaServiceAccount(
serviceAccountCredentials,
['https://www.googleapis.com/auth/firebase.messaging'],
);
_accessToken = client.credentials.accessToken;
_expiryDate = client.credentials.accessToken.expiry;
client.close();
Log.print('_accessToken!.data: ${_accessToken!.data}');
return _accessToken!.data;
} catch (e) {
throw Exception('Failed to obtain access token');
}
}
}

View File

@@ -0,0 +1,808 @@
import 'dart:convert';
import 'dart:io';
import 'package:get/get.dart';
import 'package:http/http.dart' as http;
import 'package:jwt_decoder/jwt_decoder.dart';
import 'package:secure_string_operations/secure_string_operations.dart';
import '../../constant/api_key.dart';
import '../../constant/box_name.dart';
import '../../constant/char_map.dart';
import '../../constant/info.dart';
import '../../constant/links.dart';
import '../../env/env.dart';
import '../../main.dart';
import '../../print.dart';
import 'device_info.dart';
import 'encrypt_decrypt.dart';
import 'security_checks.dart';
class CRUD {
var dev = '';
getJWT() async {
// إذا كان الأدمن مسجل دخوله بالفعل، لا تقم بتوليد توكن "ضيف" قديم
if (box.read(BoxName.driverID) != null) {
Log.print('Admin session active. Skipping guest JWT.');
return;
}
dev = Platform.isAndroid ? 'android' : 'ios';
var payload = {
'id': 'admin',
'password': AK.passnpassenger,
'aud': '${AK.allowed}$dev',
};
Log.print('payload: ${payload}');
var response1 = await http.post(
Uri.parse(AppLink.loginJwtDriver),
body: payload,
);
if (response1.statusCode == 200) {
final decodedResponse1 = jsonDecode(response1.body);
final jwt = decodedResponse1['jwt'];
Log.print('jwt: ${jwt}');
await box.write(BoxName.jwt, X.c(X.c(X.c(jwt, cn), cC), cs));
// await AppInitializer().getKey();
}
}
Future<dynamic> get({
required String link,
Map<String, dynamic>? payload,
}) async {
String token = '';
var rawJwt = box.read(BoxName.jwt);
if (rawJwt == null) {
await getJWT();
rawJwt = box.read(BoxName.jwt);
}
if (rawJwt != null) {
token = r(rawJwt.toString()).split(AppInformation.addd)[0];
try {
if (JwtDecoder.isExpired(token)) {
// If we have an admin ID, we should probably re-login or refresh,
// but for now let's just fall back to guest JWT if needed.
if (box.read(BoxName.driverID) == null) {
await getJWT();
token = r(box.read(BoxName.jwt).toString())
.split(AppInformation.addd)[0];
}
}
} catch (e) {
print('❌ JWT Decode Error: $e');
// If decryption failed, try using raw (maybe it was saved plain)
token = rawJwt.toString().split(AppInformation.addd)[0];
}
}
var url = Uri.parse(link);
Log.print('--- [CRUD GET] ---');
Log.print('URL: $link');
Log.print('Payload: $payload');
var response = await http.post(
url,
body: payload,
headers: {
"Content-Type": "application/x-www-form-urlencoded",
'Authorization': 'Bearer $token',
'X-Device-FP': box.read(BoxName.fingerPrint) ?? '',
},
);
Log.print('Status Code: ${response.statusCode}');
Log.print('Response Body: ${response.body}');
Log.print('------------------');
if (response.statusCode == 200) {
try {
var jsonData = jsonDecode(response.body);
if (jsonData['status'] == 'success') {
return response.body;
}
return jsonData['status'] ?? 'failure';
} catch (e) {
return 'failure';
}
} else if (response.statusCode == 401) {
return 'token_expired';
} else {
return 'failure';
}
}
Future<dynamic> post(
{required String link, Map<String, dynamic>? payload}) async {
var url = Uri.parse(link);
try {
String token = '';
var rawJwt = box.read(BoxName.jwt);
if (rawJwt != null) {
token = r(rawJwt.toString()).split(AppInformation.addd)[0];
try {
if (JwtDecoder.isExpired(token)) {
if (box.read(BoxName.driverID) == null) {
await getJWT();
token = r(box.read(BoxName.jwt).toString())
.split(AppInformation.addd)[0];
}
}
} catch (e) {
token = rawJwt.toString().split(AppInformation.addd)[0];
}
}
Log.print('--- [CRUD POST] ---');
Log.print('URL: $link');
Log.print('Payload: $payload');
var response = await http.post(
url,
body: payload,
headers: {
"Content-Type": "application/x-www-form-urlencoded",
'Authorization': 'Bearer $token',
'X-Device-FP': box.read(BoxName.fingerPrint) ?? '',
},
);
Log.print('Status Code: ${response.statusCode}');
Log.print('Response Body: ${response.body}');
Log.print('-------------------');
if (response.statusCode == 200) {
try {
var jsonData = jsonDecode(response.body);
if (jsonData['status'] == 'success') {
return jsonData;
} else {
return jsonData['status'];
}
} catch (e) {
return 'failure';
}
} else if (response.statusCode == 401) {
return 'token_expired';
} else {
return 'failure';
}
} catch (e) {
// addError('HTTP request error: $e', 'crud().post - HTTP');
return 'failure';
}
}
getJwtWallet() async {
// 1. فحص التوكن المخزن أولاً (Caching) لتقليل طلبات الـ SSO
var cachedWalletJwt = box.read('wallet_jwt');
var cachedWalletExpiry = box.read('wallet_jwt_expiry');
if (cachedWalletJwt != null && cachedWalletExpiry != null) {
int expiryMs = int.tryParse(cachedWalletExpiry.toString()) ?? 0;
// إذا لم ينته بعد (مع هامش أمان 60 ثانية)
if (DateTime.now().millisecondsSinceEpoch < expiryMs - 60000) {
Log.print(
'Using cached Wallet JWT. Expiry: ${DateTime.fromMillisecondsSinceEpoch(expiryMs)}');
return cachedWalletJwt.toString();
}
}
String fingerPrint = await DeviceHelper.getDeviceFingerprint();
final mainTokenEnc = box.read(BoxName.jwt);
if (mainTokenEnc == null) return null;
String mainToken = mainTokenEnc.toString();
// فك تشفير التوكن بنفس طريقة دالة get() التي تعمل بنجاح
try {
mainToken = r(mainTokenEnc.toString()).split(AppInformation.addd)[0];
} catch (e) {
// إذا فشل فك التشفير، نستخدم القيمة كما هي (ربما مخزنة بدون تشفير)
mainToken = mainTokenEnc.toString().split(AppInformation.addd)[0];
}
Log.print('Wallet SSO mainToken length: ${mainToken.length}');
Log.print(
'Wallet SSO token starts with: ${mainToken.substring(0, mainToken.length > 10 ? 10 : mainToken.length)}');
// استخدام الـ SSO للسيرفر الرئيسي إذا كان الأدمن مسجل دخوله
var response1 = await http.post(
Uri.parse(AppLink.loginWalletAdminV3),
headers: {
'Authorization': 'Bearer $mainToken',
'X-Device-FP': fingerPrint,
},
);
Log.print('Wallet SSO login status: ${response1.statusCode}');
if (response1.statusCode == 200) {
final decoded = jsonDecode(response1.body);
final msg = decoded['message'];
String? jwt;
String? hmac;
if (msg is Map) {
jwt = msg['jwt'];
hmac = msg['hmac'];
} else {
jwt = decoded['jwt'];
hmac = decoded['hmac'];
}
if (hmac != null) await box.write(BoxName.hmac, hmac);
// تخزين التوكن الجديد في الكاش لمدة 10 دقائق (600 ثانية)
if (jwt != null) {
await box.write('wallet_jwt', jwt);
await box.write('wallet_jwt_expiry',
(DateTime.now().millisecondsSinceEpoch + (600 * 1000)).toString());
Log.print('Wallet JWT cached successfully.');
}
return jwt?.toString();
} else {
// Fallback: المحاولة بالطريقة القديمة إذا فشل الـ SSO
var payload = {
'id': box.read(BoxName.driverID) ?? '1',
'password': AK.passnpassenger,
'aud': '${Env.allowedWallet}${Platform.isAndroid ? 'android' : 'ios'}',
'fingerPrint': fingerPrint
};
var fallbackRes = await http.post(
Uri.parse(AppLink.loginWalletAdmin),
body: payload,
);
if (fallbackRes.statusCode == 200) {
final decoded = jsonDecode(fallbackRes.body);
if (decoded['jwt'] != null) return decoded['jwt'].toString();
}
}
return null;
}
Future<dynamic> getWallet({
required String link,
Map<String, dynamic>? payload,
bool isRetry = false,
}) async {
var s = await getJwtWallet();
final hmac = box.read(BoxName.hmac);
var url = Uri.parse(link);
Log.print('--- getWallet Execution ---');
Log.print('URL: $url');
Log.print('JWT: $s');
Log.print('HMAC: $hmac');
Log.print('Is Retry: $isRetry');
Log.print('Payload: $payload');
if (payload != null && hmac != null) {
payload['hmac'] = hmac.toString();
}
try {
var response = await http.post(
url,
body: payload,
headers: {
"Content-Type": "application/x-www-form-urlencoded",
'Authorization': 'Bearer $s',
'X-HMAC-Auth': hmac.toString(),
'X-Device-FP': box.read(BoxName.fingerPrint) ?? '',
},
);
Log.print('Status Code: ${response.statusCode}');
Log.print('Response Body: ${response.body}');
if (response.statusCode == 200) {
try {
var jsonData = jsonDecode(response.body);
if (jsonData['status'] == 'success') {
return jsonData;
}
Log.print('Logic Error: Status is not success. Data: $jsonData');
return jsonData['status'] ?? 'failure';
} catch (e) {
Log.print('JSON Decode Error in getWallet: $e');
return 'failure';
}
} else if (response.statusCode == 401 && !isRetry) {
Log.print('Token expired (401). Clearing cache and retrying...');
await box.remove('wallet_jwt');
await box.remove('wallet_jwt_expiry');
return await getWallet(link: link, payload: payload, isRetry: true);
} else {
Log.print('HTTP Error in getWallet. Status: ${response.statusCode}');
return 'failure';
}
} catch (e) {
Log.print('HTTP Request Exception in getWallet: $e');
return 'failure';
}
}
Future<dynamic> postWallet(
{required String link, Map<String, dynamic>? payload}) async {
var s = await getJwtWallet();
Log.print('jwt: ${s}');
final hmac = box.read(BoxName.hmac);
Log.print('hmac: ${hmac}');
var url = Uri.parse(link);
Log.print('url: ${url}');
// إضافة الـ HMAC للـ payload لزيادة التوافقية
if (payload != null && hmac != null) {
payload['hmac'] = hmac.toString();
}
try {
// await LoginDriverController().getJWT();
var response = await http.post(
url,
body: payload,
headers: {
"Content-Type": "application/x-www-form-urlencoded",
'Authorization': 'Bearer $s',
'X-HMAC-Auth': hmac.toString(),
'X-Device-FP': box.read(BoxName.fingerPrint) ?? '',
},
);
// Log.print('response.request:${response.request}');
// Log.print('response.body: ${response.body}');
// Log.print('payload:$payload');
if (response.statusCode == 200) {
try {
var jsonData = jsonDecode(response.body);
if (jsonData['status'] == 'success') {
return jsonData;
}
return jsonData['status'] ?? 'failure';
} catch (e) {
return 'failure';
}
} else if (response.statusCode == 401) {
await getJwtWallet();
return 'token_expired';
} else {
return 'failure';
}
} catch (e) {
// addError('HTTP request error: $e', 'crud().post - HTTP');
return 'failure';
}
}
// }
Future sendWhatsAppAuth(String to, message) async {
var res = await CRUD().post(
link: AppLink.send_whatsapp_message,
payload: {'receiver': to, 'message': message});
if (res != 'failure') {
Get.snackbar('Success', 'Message sent successfully');
} else {
Get.snackbar('Error', 'Failed to send message');
}
}
Future<dynamic> getAgoraToken({
required String channelName,
required String uid,
}) async {
var uid = box.read(BoxName.phone) ?? box.read(BoxName.phoneDriver);
var res = await http.get(
Uri.parse(
'https://repulsive-pig-rugby-shirt.cyclic.app/token?channelName=$channelName'),
headers: {'Authorization': 'Bearer ${AK.agoraAppCertificate}'});
if (res.statusCode == 200) {
var response = jsonDecode(res.body);
return response['token'];
} else {}
}
Future<dynamic> getLlama({
required String link,
required String payload,
required String prompt,
}) async {
var url = Uri.parse(
link,
);
var headers = {
'Content-Type': 'application/json',
'Authorization':
'Bearer LL-X5lJ0Px9CzKK0HTuVZ3u2u4v3tGWkImLTG7okGRk4t25zrsLqJ0qNoUzZ2x4ciPy'
// 'Authorization': 'Bearer ${Env.llamaKey}'
};
var data = json.encode({
"model": "Llama-3-70b-Inst-FW",
// "model": "llama-13b-chat",
"messages": [
{
"role": "user",
"content":
"Extract the desired information from the following passage as json decoded like $prompt just in this:\n\n$payload"
}
],
"temperature": 0.9
});
var response = await http.post(
url,
body: data,
headers: headers,
);
if (response.statusCode == 200) {
return response.body;
}
return response.statusCode;
}
Future allMethodForAI(String prompt, driverID, imagePath) async {
// await ImageController().choosImage(linkPHP, imagePath);
Future.delayed(const Duration(seconds: 2));
var extractedString = await arabicTextExtractByVisionAndAI(
imagePath: imagePath, driverID: driverID);
var json = jsonDecode(extractedString);
var textValues = getAllTextValuesWithLineNumbers(json);
// List<String> textValues = getAllTextValues(json);
// await AI().geminiAiExtraction(prompt, textValues);
}
Map<String, List<Map<String, String>>> getAllTextValuesWithLineNumbers(
Map json) {
Map<String, List<Map<String, String>>> output = {};
int lineNumber = 1;
if (json.containsKey('regions')) {
List<dynamic> regions = json['regions'];
for (Map<String, dynamic> region in regions) {
if (region.containsKey('lines')) {
List<dynamic> lines = region['lines'];
List<Map<String, String>> linesWithText = [];
for (Map<String, dynamic> line in lines) {
if (line.containsKey('words')) {
List<dynamic> words = line['words'];
String lineText = "";
for (Map<String, dynamic> word in words) {
if (word.containsKey('text')) {
lineText += word['text'] + " ";
}
}
lineText = lineText.trim();
linesWithText.add(
{"line_number": lineNumber.toString(), "text": lineText});
lineNumber++;
}
}
output["region_${region.hashCode}"] = linesWithText;
}
}
}
return output;
}
// List<String> getAllTextValues(Map json) {
// List<String> textValues = [];
// if (json.containsKey('regions')) {
// List<dynamic> regions = json['regions'];
// for (Map<String, dynamic> region in regions) {
// if (region.containsKey('lines')) {
// List<dynamic> lines = region['lines'];
// for (Map<String, dynamic> line in lines) {
// if (line.containsKey('words')) {
// List<dynamic> words = line['words'];
// for (Map<String, dynamic> word in words) {
// if (word.containsKey('text')) {
// textValues.add(word['text']);
// }
// }
// }
// }
// }
// }
// }
// return textValues;
// }
Future<dynamic> arabicTextExtractByVisionAndAI({
required String imagePath,
required String driverID,
}) async {
var headers = {
'Content-Type': 'application/json',
'Ocp-Apim-Subscription-Key': AK.ocpApimSubscriptionKey
};
String imagePathFull =
'${AppLink.server}/card_image/$imagePath-$driverID.jpg';
var request = http.Request(
'POST',
Uri.parse(
'https://eastus.api.cognitive.microsoft.com/computervision/imageanalysis:analyze?features=caption,read&model-version=latest&language=en&api-version=2024-02-01'));
request.body = json.encode({"url": imagePathFull});
request.headers.addAll(headers);
http.StreamedResponse response = await request.send();
if (response.statusCode == 200) {
return await response.stream.bytesToString();
} else {}
}
Future<dynamic> getChatGPT({
required String link,
required String payload,
}) async {
var url = Uri.parse(
link,
);
var headers = {
'Content-Type': 'application/json',
'Authorization': 'Bearer ${Env.chatGPTkeySeferNew}'
};
var data = json.encode({
"model": "gpt-3.5-turbo",
"messages": [
{
"role": "user",
"content":
"Extract the desired information from the following passage as json decoded like vin,make,made,year,expiration_date,color,owner,registration_date just in this:\n\n$payload"
}
],
"temperature": 0.9
});
var response = await http.post(
url,
body: data,
headers: headers,
);
if (response.statusCode == 200) {
return response.body;
}
return response.statusCode;
}
Future<dynamic> kazumiSMS({
required String link,
Map<String, dynamic>? payload,
}) async {
var url = Uri.parse(
link,
);
var headers = {'Content-Type': 'application/json'};
var request = http.Request('POST', url);
request.body = json.encode({
"username": "Sefer",
"password": AK.smsPasswordEgypt,
});
request.headers.addAll(headers);
http.StreamedResponse response = await request.send();
if (response.statusCode == 200) {
var responseBody = await response.stream.bytesToString();
var data = json.decode(responseBody);
return data;
} else {}
}
Future<dynamic> sendSmsEgypt(String phone, otp) async {
// String sender = await getSender();
var headers = {'Content-Type': 'application/json'};
var body = jsonEncode({
"username": "Sefer",
"password": "E)Pu=an/@Z",
"message": otp,
"language": "e",
"sender": "Sefer Egy",
"receiver": phone
});
var res = await http.post(
Uri.parse(AppLink.sendSms),
body: body,
headers: headers,
);
}
Future<dynamic> postPayMob({
required String link,
Map<String, dynamic>? payload,
}) async {
// String? basicAuthCredentials =
// await storage.read(key: BoxName.basicAuthCredentials);
var url = Uri.parse(
link,
);
var response = await http.post(url,
body: payload, headers: {'Content-Type': 'application/json'});
var jsonData = jsonDecode(response.body);
if (response.statusCode == 200) {
if (jsonData['status'] == 'success') {
return response.body;
} else {
return (jsonData['status']);
}
} else {
return response.statusCode;
}
}
sendEmail(
String link,
Map<String, String>? payload,
) async {
var headers = {
"Content-Type": "application/x-www-form-urlencoded",
'Authorization':
'Basic ${base64Encode(utf8.encode(AK.basicAuthCredentials))}',
};
var request = http.Request('POST', Uri.parse(link));
request.bodyFields = payload!;
request.headers.addAll(headers);
http.StreamedResponse response = await request.send();
if (response.statusCode == 200) {
} else {}
}
Future<dynamic> postFromDialogue({
required String link,
Map<String, dynamic>? payload,
}) async {
// String? basicAuthCredentials =
// await storage.read(key: BoxName.basicAuthCredentials);
var url = Uri.parse(
link,
);
var response = await http.post(
url,
body: payload,
headers: {
"Content-Type": "application/x-www-form-urlencoded",
'Authorization':
'Basic ${base64Encode(utf8.encode(AK.basicAuthCredentials))}',
},
);
if (response.body.isNotEmpty) {
var jsonData = jsonDecode(response.body);
if (response.statusCode == 200) {
if (jsonData['status'] == 'success') {
Get.back();
// Get.snackbar(
// jsonData['status'],
// jsonData['message'],
// );
return response.body;
}
}
return (jsonData['status']);
}
}
Future<void> sendVerificationRequest(String phoneNumber) async {
final accountSid = AK.accountSIDTwillo;
final authToken = AK.authTokenTwillo;
final verifySid = AK.twilloRecoveryCode;
final Uri verificationUri = Uri.parse(
'https://verify.twilio.com/v2/Services/$verifySid/Verifications');
// Send the verification request
final response = await http.post(
verificationUri,
headers: {
'Authorization':
'Basic ${base64Encode(utf8.encode('$accountSid:$authToken'))}',
'Content-Type': 'application/x-www-form-urlencoded',
},
body: {
'To': phoneNumber,
'Channel': 'sms',
},
);
if (response.statusCode == 201) {
} else {}
// Prompt the user to enter the OTP
const otpCode = "123456"; // Replace with user input
// Check the verification code
final checkUri = Uri.parse(
'https://verify.twilio.com/v2/Services/$verifySid/VerificationCheck');
final checkResponse = await http.post(
checkUri,
headers: {
'Authorization':
'Basic ${base64Encode(utf8.encode('$accountSid:$authToken'))}',
'Content-Type': 'application/x-www-form-urlencoded',
},
body: {
'To': phoneNumber,
'Code': otpCode,
},
);
if (checkResponse.statusCode == 201) {
} else {}
}
Future<dynamic> getGoogleApi({
required String link,
Map<String, dynamic>? payload,
}) async {
var url = Uri.parse(
link,
);
var response = await http.post(
url,
body: payload,
);
var jsonData = jsonDecode(response.body);
if (jsonData['status'] == 'OK') {
return jsonData;
}
return (jsonData['status']);
}
Future<dynamic> update({
required String endpoint,
required Map<String, dynamic> data,
required String id,
}) async {
// String? basicAuthCredentials =
// await storage.read(key: BoxName.basicAuthCredentials);
var url = Uri.parse('$endpoint/$id');
var response = await http.put(
url,
body: json.encode(data),
headers: {
'Authorization':
'Basic ${base64Encode(utf8.encode(AK.basicAuthCredentials))}',
},
);
return json.decode(response.body);
}
Future<dynamic> delete({
required String endpoint,
required String id,
}) async {
// String? basicAuthCredentials =
// await storage.read(key: BoxName.basicAuthCredentials);
var url = Uri.parse('$endpoint/$id');
var response = await http.delete(
url,
headers: {
'Authorization':
'Basic ${base64Encode(utf8.encode(AK.basicAuthCredentials))}',
},
);
return json.decode(response.body);
}
List phoneDriversTest = [
201023248456,
201023248456,
];
}

View File

@@ -0,0 +1,26 @@
import 'package:flutter/material.dart';
class LineChartPainter extends CustomPainter {
final List<double> data;
LineChartPainter(this.data);
@override
void paint(Canvas canvas, Size size) {
// Calculate the scale factor.
final scaleFactor = size.height / 240;
// Draw the line chart.
for (var i = 0; i < data.length - 1; i++) {
final x1 = i * size.width / data.length;
final y1 = data[i] * scaleFactor;
final x2 = (i + 1) * size.width / data.length;
final y2 = data[i + 1] * scaleFactor;
canvas.drawLine(Offset(x1, y1), Offset(x2, y2), Paint());
}
}
@override
bool shouldRepaint(LineChartPainter oldDelegate) => false;
}

View File

@@ -0,0 +1,291 @@
// import 'dart:io';
// import 'package:device_info_plus/device_info_plus.dart';
import 'dart:async';
import 'dart:io';
import 'package:device_info_plus/device_info_plus.dart';
class DeviceHelper {
static Future<String> getDeviceFingerprint() async {
final DeviceInfoPlugin deviceInfoPlugin = DeviceInfoPlugin();
var deviceData;
try {
if (Platform.isAndroid) {
// Fetch Android-specific device information
AndroidDeviceInfo androidInfo = await deviceInfoPlugin.androidInfo;
deviceData = androidInfo.toMap(); // Convert to a map for easier access
// Log.print('deviceData: ${jsonEncode(deviceData)}');
} else if (Platform.isIOS) {
// Fetch iOS-specific device information
IosDeviceInfo iosInfo = await deviceInfoPlugin.iosInfo;
deviceData = iosInfo.toMap(); // Convert to a map for easier access
} else if (Platform.isMacOS) {
// Fetch macOS-specific device information
MacOsDeviceInfo macInfo = await deviceInfoPlugin.macOsInfo;
deviceData = macInfo.toMap();
} else {
throw UnsupportedError('Unsupported platform');
}
String deviceId = 'unknown';
if (Platform.isAndroid) {
deviceId = deviceData['fingerprint'] ?? 'unknown';
} else if (Platform.isIOS) {
deviceId = deviceData['identifierForVendor'] ?? 'unknown';
} else if (Platform.isMacOS) {
deviceId = deviceData['systemGUID'] ?? 'unknown';
}
final String deviceModel = deviceData['model'] ?? 'unknown';
final String fingerprint = '${deviceId}_$deviceModel';
print(fingerprint);
return (fingerprint);
} catch (e) {
throw Exception('Failed to generate device fingerprint');
}
}
}
// class SecurityHelper {
// /// Performs security checks and handles potential risks
// static Future<void> performSecurityChecks() async {
// bool isNotTrust = false;
// bool isJailBroken = false;
// bool isRealDevice = true;
// bool isOnExternalStorage = false;
// bool checkForIssues = false;
// bool isDevMode = false;
// bool isTampered = false;
// String bundleId = "";
// try {
// isNotTrust = await JailbreakRootDetection.instance.isNotTrust;
// isJailBroken = await JailbreakRootDetection.instance.isJailBroken;
// isRealDevice = await JailbreakRootDetection.instance.isRealDevice;
// isOnExternalStorage =
// await JailbreakRootDetection.instance.isOnExternalStorage;
// List<JailbreakIssue> issues =
// await JailbreakRootDetection.instance.checkForIssues;
// checkForIssues = issues.isNotEmpty;
// isDevMode = await JailbreakRootDetection.instance.isDevMode;
// // Get Bundle ID
// PackageInfo packageInfo = await PackageInfo.fromPlatform();
// bundleId = packageInfo.packageName;
// if (bundleId.isNotEmpty) {
// // Pass the CORRECT bundle ID to isTampered
// isTampered = await JailbreakRootDetection.instance.isTampered(bundleId);
// }
// } catch (e) {
// debugPrint("Error during security checks: $e");
// // Consider handling specific exceptions, not just general errors.
// }
// // Save values to storage (using GetStorage)
// await box.write('isNotTrust', isNotTrust); // Use await for write operations
// await box.write('isTampered', isTampered); // Use await
// await box.write('isJailBroken', isJailBroken); // Use await
// // debugPrint("Security Check Results:");
// // debugPrint("isNotTrust: $isNotTrust");
// // debugPrint("isJailBroken: $isJailBroken");
// // debugPrint("isRealDevice: $isRealDevice");
// // debugPrint("isOnExternalStorage: $isOnExternalStorage");
// // debugPrint("checkForIssues: $checkForIssues");
// // debugPrint("isDevMode: $isDevMode");
// // debugPrint("isTampered: $isTampered");
// // debugPrint("Bundle ID: $bundleId"); // Print the bundle ID
// // Check for security risks and potentially show a warning
// if (isJailBroken || isRealDevice == false || isTampered) {
// // print("security_warning".tr); //using easy_localization
// // Use a more robust approach to show a warning, like a dialog:
// _showSecurityWarning();
// }
// }
// /// Deletes all app data
// static Future<void> clearAllData() async {
// //await storage.deleteAll(); // What's 'storage'? Be specific. Likely GetStorage as well.
// await box.erase(); // Clear GetStorage data
// exit(0); // This will terminate the app. Be VERY careful with this.
// }
// // static void _showSecurityWarning() {
// // // Show a dialog, navigate to an error screen, etc.
// // // Example using Get.dialog (if you use GetX):
// //
// // Get.dialog(
// // AlertDialog(
// // title: Text("Security Warning".tr), // Or use localized string
// // content: Text(
// // "Potential security risks detected. The application may not function correctly."
// // .tr), //Or use localized string
// // actions: [
// // TextButton(
// // onPressed: () async {
// // await storage.deleteAll();
// // await box.erase();
// // Get.back(); // Close the dialog
// // // Or, if you really must, exit the app (but give the user a chance!)
// // exit(0);
// // },
// // child: Text("OK"), // Or use a localized string
// // ),
// // ],
// // ),
// // barrierDismissible: false, // Prevent closing by tapping outside
// // );
// // }
// static void _showSecurityWarning() {
// // Use an RxInt to track the remaining seconds. This is the KEY!
// RxInt secondsRemaining = 10.obs;
// Get.dialog(
// CupertinoAlertDialog(
// title: Text("Security Warning".tr),
// content: Column(
// mainAxisSize: MainAxisSize.min,
// children: [
// Obx(() => Text(
// "Potential security risks detected. The application will close in @seconds seconds."
// .trParams({
// // Use trParams for placeholders
// 'seconds': secondsRemaining.value.toString(),
// }),
// // Wrap the Text widget in Obx
// )),
// SizedBox(height: 24), // More spacing before the progress bar
// Obx(() => SizedBox(
// width: double.infinity, // Make progress bar full width
// child: CupertinoActivityIndicator(
// // in case of loading
// radius: 15,
// animating: true,
// ))),
// SizedBox(height: 8),
// Obx(() => ClipRRect(
// borderRadius: BorderRadius.circular(8), // Rounded corners
// child: LinearProgressIndicator(
// value: secondsRemaining.value / 10,
// backgroundColor: Colors.grey.shade300, // Lighter background
// valueColor: AlwaysStoppedAnimation<Color>(
// CupertinoColors.systemRed), // iOS-style red
// minHeight: 8, // Slightly thicker progress bar
// ),
// )),
// ],
// ),
// ),
// barrierDismissible: false,
// );
// Timer.periodic(Duration(seconds: 1), (timer) {
// secondsRemaining.value--;
// if (secondsRemaining.value <= 0) {
// timer.cancel();
// // Get.back();
// _clearDataAndExit();
// }
// });
// }
// static Future<void> _clearDataAndExit() async {
// await storage.deleteAll();
// await box.erase();
// exit(0); // Exit the app
// print('exit');
// }
// }
// class DeviceInfoPlus {
// static List<Map<String, dynamic>> deviceDataList = [];
// static Future<List<Map<String, dynamic>>> getDeviceInfo() async {
// final DeviceInfoPlugin deviceInfoPlugin = DeviceInfoPlugin();
// try {
// if (Platform.isAndroid) {
// AndroidDeviceInfo androidInfo = await deviceInfoPlugin.androidInfo;
// Map<String, dynamic> deviceData = {
// 'platform': 'Android',
// 'brand': androidInfo.brand,
// 'model': androidInfo.model,
// 'androidId': androidInfo.device,
// 'versionRelease': androidInfo.version.release,
// 'sdkVersion': androidInfo.version.sdkInt,
// 'manufacturer': androidInfo.manufacturer,
// 'isPhysicalDevice': androidInfo.isPhysicalDevice,
// 'serialNumber': androidInfo.serialNumber,
// 'fingerprint': androidInfo.fingerprint,
// 'type': androidInfo.type,
// 'data': androidInfo.data,
// 'version': androidInfo.version,
// 'tags': androidInfo.tags,
// 'display': androidInfo.display,
// };
// deviceDataList.add(deviceData);
// } else if (Platform.isIOS) {
// IosDeviceInfo iosInfo = await deviceInfoPlugin.iosInfo;
// Map<String, dynamic> deviceData = {
// 'brand': 'Apple',
// 'model': iosInfo.model,
// 'systemName': iosInfo.systemName,
// 'systemVersion': iosInfo.systemVersion,
// 'utsname': iosInfo.utsname,
// 'isPhysicalDevice': iosInfo.isPhysicalDevice,
// 'identifierForVendor': iosInfo.identifierForVendor,
// 'name': iosInfo.name,
// 'localizedModel': iosInfo.localizedModel,
// };
// deviceDataList.add(deviceData);
// } else if (Platform.isMacOS) {
// MacOsDeviceInfo macInfo = await deviceInfoPlugin.macOsInfo;
// Map<String, dynamic> deviceData = {
// 'platform': 'macOS',
// 'model': macInfo.model,
// 'version': macInfo.systemGUID,
// };
// deviceDataList.add(deviceData);
// } else if (Platform.isWindows) {
// WindowsDeviceInfo windowsInfo = await deviceInfoPlugin.windowsInfo;
// Map<String, dynamic> deviceData = {
// 'platform': 'Windows',
// 'manufacturer': windowsInfo.computerName,
// 'version': windowsInfo.majorVersion,
// 'deviceId': windowsInfo.deviceId,
// 'userName': windowsInfo.userName,
// 'productName': windowsInfo.productName,
// 'installDate': windowsInfo.installDate,
// 'productId': windowsInfo.productId,
// 'numberOfCores': windowsInfo.numberOfCores,
// 'systemMemoryInMegabytes': windowsInfo.systemMemoryInMegabytes,
// };
// deviceDataList.add(deviceData);
// } else if (Platform.isLinux) {
// LinuxDeviceInfo linuxInfo = await deviceInfoPlugin.linuxInfo;
// Map<String, dynamic> deviceData = {
// 'platform': 'Linux',
// 'manufacturer': linuxInfo.name,
// 'version': linuxInfo.version,
// };
// deviceDataList.add(deviceData);
// }
// } catch (e) {
// }
// return deviceDataList;
// }
// // Method to print all device data
// static void printDeviceInfo() {
// for (Map<String, dynamic> deviceData in deviceDataList) {
// 'Version: ${deviceData['version'] ?? deviceData['versionRelease'] ?? 'N/A'}');
// }
// }
// }

View File

@@ -0,0 +1,42 @@
import 'package:flutter/services.dart';
class DigitObscuringFormatter extends TextInputFormatter {
@override
TextEditingValue formatEditUpdate(
TextEditingValue oldValue, TextEditingValue newValue) {
final maskedText = maskDigits(newValue.text);
return newValue.copyWith(
text: maskedText,
selection: updateCursorPosition(maskedText, newValue.selection));
}
String maskDigits(String text) {
final totalDigits = text.length;
final visibleDigits = 4;
final hiddenDigits = totalDigits - visibleDigits * 2;
final firstVisibleDigits = text.substring(0, visibleDigits);
final lastVisibleDigits = text.substring(totalDigits - visibleDigits);
final maskedDigits = List.filled(hiddenDigits, '*').join();
return '$firstVisibleDigits$maskedDigits$lastVisibleDigits';
}
TextSelection updateCursorPosition(
String maskedText, TextSelection currentSelection) {
final cursorPosition = currentSelection.baseOffset;
final cursorOffset =
currentSelection.extentOffset - currentSelection.baseOffset;
final totalDigits = maskedText.length;
const visibleDigits = 4;
final hiddenDigits = totalDigits - visibleDigits * 2;
final updatedPosition = cursorPosition <= visibleDigits
? cursorPosition
: hiddenDigits + visibleDigits + (cursorPosition - visibleDigits);
return TextSelection.collapsed(
offset: updatedPosition, affinity: currentSelection.affinity);
}
}

View File

@@ -0,0 +1,41 @@
// import 'dart:io';
//
// import 'package:get/get.dart';
// import 'package:image_picker/image_picker.dart';
// import 'package:google_ml_kit/google_ml_kit.dart';
//
// class ImagePickerController extends GetxController {
// RxBool textScanning = false.obs;
// RxString scannedText = ''.obs;
//
// Future<void> getImage(ImageSource source) async {
// try {
// final pickedImage = await ImagePicker().pickImage(source: source);
// if (pickedImage != null) {
// textScanning.value = true;
// final imageFile = File(pickedImage.path);
// getRecognisedText(imageFile);
// }
// } catch (e) {
// textScanning.value = false;
// scannedText.value = "Error occurred while scanning";
// }
// }
//
// Future<void> getRecognisedText(File image) async {
// final inputImage = InputImage.fromFilePath(image.path);
// final textDetector = GoogleMlKit.vision.textRecognizer();
// final RecognizedText recognisedText =
// await textDetector.processImage(inputImage);
// await textDetector.close();
//
// scannedText.value = '';
// for (TextBlock block in recognisedText.blocks) {
// for (TextLine line in block.lines) {
// scannedText.value += line.text + '\n';
// }
// }
//
// textScanning.value = false;
// }
// }

View File

@@ -0,0 +1,79 @@
import 'package:encrypt/encrypt.dart' as encrypt;
import 'package:flutter/foundation.dart';
import 'package:secure_string_operations/secure_string_operations.dart';
import '../../constant/char_map.dart';
import '../../env/env.dart';
import '../../main.dart';
import '../../print.dart';
class EncryptionHelper {
static EncryptionHelper? _instance;
late final encrypt.Key key;
late final encrypt.IV iv;
EncryptionHelper._(this.key, this.iv);
static EncryptionHelper get instance {
if (_instance == null) {
throw Exception(
"EncryptionHelper is not initialized. Call `await EncryptionHelper.initialize()` in main.");
}
return _instance!;
}
/// Initializes and stores the instance globally
static Future<void> initialize() async {
if (_instance != null) {
debugPrint("EncryptionHelper is already initialized.");
return; // Prevent re-initialization
}
debugPrint("Initializing EncryptionHelper...");
var keyOfApp = r(Env.keyOfApp).toString().split(Env.addd)[0];
// Log.print('keyOfApp: ${keyOfApp}');
var initializationVector =
r(Env.initializationVector).toString().split(Env.addd)[0];
// Log.print('initializationVector: ${initializationVector}');
// Set the global instance
_instance = EncryptionHelper._(
encrypt.Key.fromUtf8(keyOfApp),
encrypt.IV.fromUtf8(initializationVector),
);
debugPrint("EncryptionHelper initialized successfully.");
}
/// Encrypts a string
String encryptData(String plainText) {
try {
final encrypter =
encrypt.Encrypter(encrypt.AES(key, mode: encrypt.AESMode.cbc));
final encrypted = encrypter.encrypt(plainText, iv: iv);
return encrypted.base64;
} catch (e) {
debugPrint('Encryption Error: $e');
return '';
}
}
/// Decrypts a string
String decryptData(String encryptedText) {
try {
final encrypter =
encrypt.Encrypter(encrypt.AES(key, mode: encrypt.AESMode.cbc));
final encrypted = encrypt.Encrypted.fromBase64(encryptedText);
return encrypter.decrypt(encrypted, iv: iv);
} catch (e) {
debugPrint('Decryption Error: $e');
return '';
}
}
}
r(String string) {
return X.r(X.r(X.r(string, cn), cC), cs).toString();
}
c(String string) {
return X.c(X.c(X.c(string, cn), cC), cs).toString();
}

View File

@@ -0,0 +1,829 @@
// import 'dart:convert';
// import 'dart:io';
// import 'package:get/get.dart';
// import 'package:image_cropper/image_cropper.dart';
// import 'package:image_picker/image_picker.dart';
// import 'package:http/http.dart' as http;
// import 'package:image/image.dart' as img;
// import 'package:path_provider/path_provider.dart';
// import '../../constant/api_key.dart';
// import '../../constant/colors.dart';
// class AI extends GetxController {
// final picker = ImagePicker();
// Map<String, dynamic> responseMap = {};
// Map<String, dynamic> responseCarLicenseMap = {};
// Map<String, dynamic> responseBackCarLicenseMap = {};
// Map<String, dynamic> responseIdCardeMap = {};
// bool isloading = false;
// var image;
// CroppedFile? croppedFile;
// DateTime now = DateTime.now();
// Future<void> pickImage() async {
// final pickedImage = await picker.pickImage(source: ImageSource.gallery);
// if (pickedImage != null) {
// image = File(pickedImage.path);
// // Crop the image
// croppedFile = await ImageCropper().cropImage(
// sourcePath: image!.path,
// aspectRatioPresets: [
// CropAspectRatioPreset.square,
// CropAspectRatioPreset.ratio3x2,
// CropAspectRatioPreset.original,
// CropAspectRatioPreset.ratio4x3,
// CropAspectRatioPreset.ratio16x9
// ],
// uiSettings: [
// AndroidUiSettings(
// toolbarTitle: 'Cropper'.tr,
// toolbarColor: AppColor.blueColor,
// toolbarWidgetColor: AppColor.yellowColor,
// initAspectRatio: CropAspectRatioPreset.original,
// lockAspectRatio: false),
// IOSUiSettings(
// title: 'Cropper'.tr,
// ),
// ],
// );
// // image = croppedFile;
// // Resize the image
// final rawImage =
// img.decodeImage(File(croppedFile!.path).readAsBytesSync());
// final resizedImage =
// img.copyResize(rawImage!, width: 800); // Adjust the width as needed
// final appDir = await getTemporaryDirectory();
// final resizedImagePath = '${appDir.path}/resized_image.jpg';
// final resizedImageFile = File(resizedImagePath);
// resizedImageFile.writeAsBytesSync(
// img.encodeJpg(resizedImage)); // Save the resized image as JPEG
// image = resizedImageFile;
// update();
// }
// }
// Future<void> generateContent() async {
// await pickImage();
// if (image != null) {
// final imageBytes = await image.readAsBytes();
// final imageData = base64Encode(imageBytes);
// var requestBody = jsonEncode({
// 'contents': [
// {
// 'parts': [
// {
// 'inlineData': {
// 'mimeType': 'image/jpeg',
// 'data': imageData,
// },
// },
// {
// 'text':
// 'write json for all data as first name ,last name,dob,licenseID,expiration date,issued date asdress class type ,output json type',
// },
// ],
// },
// ],
// 'generationConfig': {
// 'temperature': 0.4,
// 'topK': 32,
// 'topP': 1,
// 'maxOutputTokens': 4096,
// 'stopSequences': [],
// },
// 'safetySettings': [
// {
// 'category': 'HARM_CATEGORY_HARASSMENT',
// 'threshold': 'BLOCK_MEDIUM_AND_ABOVE',
// },
// {
// 'category': 'HARM_CATEGORY_HATE_SPEECH',
// 'threshold': 'BLOCK_MEDIUM_AND_ABOVE',
// },
// {
// 'category': 'HARM_CATEGORY_SEXUALLY_EXPLICIT',
// 'threshold': 'BLOCK_MEDIUM_AND_ABOVE',
// },
// {
// 'category': 'HARM_CATEGORY_DANGEROUS_CONTENT',
// 'threshold': 'BLOCK_MEDIUM_AND_ABOVE',
// },
// ],
// });
// final response = await http.post(
// Uri.parse(
// 'https://generativelanguage.googleapis.com/v1beta/models/gemini-pro-vision:generateContent?key=${AK.geminiApi}'),
// headers: {'Content-Type': 'application/json'},
// body: requestBody,
// );
// if (response.statusCode == 200) {
// var responseData = jsonDecode(response.body);
// // Process the responseData as needed
// var result =
// responseData['candidates'][0]['content']['parts'][0]['text'];
// RegExp regex = RegExp(r"```json([^`]*)```");
// String? jsonString =
// regex.firstMatch(responseData.toString())?.group(1)?.trim();
// if (jsonString != null) {
// // Convert the JSON object to a String
// jsonString = jsonEncode(json.decode(jsonString));
// } else {
// }
// // Rest of your code...
// } else {
// }
// } else {
// }
// }
// Future<void> geminiAiExtraction(String prompt, payload) async {
// var requestBody = jsonEncode({
// 'contents': [
// {
// 'parts': [
// // {
// // 'inlineData': {
// // 'mimeType': 'image/jpeg',
// // 'data': imageData,
// // },
// // },
// {
// 'text':
// "Extract the desired information from the following passage as json decoded like $prompt .and look for this instruction first name in line 3or 2 ,fullname in line 4 from it written left to right but you modify it rtl,address in line 5 it written left to right but you modify it rtl and 6,dob,nationalid in the last line as just in this:\n\n$payload"
// },
// ],
// },
// ],
// 'generationConfig': {
// 'temperature': 0.4,
// 'topK': 32,
// 'topP': 1,
// 'maxOutputTokens': 4096,
// 'stopSequences': [],
// },
// 'safety_settings': [
// {"category": "HARM_CATEGORY_HARASSMENT", "threshold": "BLOCK_NONE"},
// {"category": "HARM_CATEGORY_HATE_SPEECH", "threshold": "BLOCK_NONE"},
// {
// "category": "HARM_CATEGORY_SEXUALLY_EXPLICIT",
// "threshold": "BLOCK_NONE"
// },
// {
// "category": "HARM_CATEGORY_DANGEROUS_CONTENT",
// "threshold": "BLOCK_NONE"
// },
// ]
// });
// final response = await http.post(
// Uri.parse(
// // 'https://generativelanguage.googleapis.com/v1beta/models/gemini-pro-vision:generateContent?key=${AK.geminiApi}'),
// 'https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-pro-latest:generateContent?key=AIzaSyCyoLcSkDzK5_SMe00nhut56SSXWPR074w'),
// headers: {'Content-Type': 'application/json'},
// body: requestBody,
// );
// if (response.statusCode == 200) {
// var responseData = jsonDecode(response.body);
// // Process the responseData as needed
// var result = responseData['candidates'][0]['content']['parts'][0]['text'];
// RegExp regex = RegExp(r"```json([^`]*)```");
// String? jsonString =
// regex.firstMatch(responseData.toString())?.group(1)?.trim();
// if (jsonString != null) {
// // Convert the JSON object to a String
// jsonString = jsonEncode(json.decode(jsonString));
// } else {
// }
// // Rest of your code...
// } else {
// }
// }
// Future<void> getDriverLicenseJordanContent() async {
// await pickImage();
// isloading = true;
// update();
// if (image != null) {
// final imageBytes = await image.readAsBytes();
// final imageData = base64Encode(imageBytes);
// var requestBody = jsonEncode({
// 'contents': [
// {
// 'parts': [
// {
// 'inlineData': {
// 'mimeType': 'image/jpeg',
// 'data': imageData,
// },
// },
// {
// 'text':
// 'write json for all data as first name ,last name,dob,id ,expiration date,issued date asdress class type,age in years ,output json type in arabic value and stay engish key and make date format like YYYY-MM-DD , for name please extract name in arabic in Name in json plus first_name ',
// },
// ],
// },
// ],
// 'generationConfig': {
// 'temperature': 0.4,
// 'topK': 32,
// 'topP': 1,
// 'maxOutputTokens': 4096,
// 'stopSequences': [],
// },
// 'safetySettings': [
// {
// 'category': 'HARM_CATEGORY_HARASSMENT',
// 'threshold': 'BLOCK_MEDIUM_AND_ABOVE',
// },
// {
// 'category': 'HARM_CATEGORY_HATE_SPEECH',
// 'threshold': 'BLOCK_MEDIUM_AND_ABOVE',
// },
// {
// 'category': 'HARM_CATEGORY_SEXUALLY_EXPLICIT',
// 'threshold': 'BLOCK_MEDIUM_AND_ABOVE',
// },
// {
// 'category': 'HARM_CATEGORY_DANGEROUS_CONTENT',
// 'threshold': 'BLOCK_MEDIUM_AND_ABOVE',
// },
// ],
// });
// final response = await http.post(
// Uri.parse(
// 'https://generativelanguage.googleapis.com/v1beta/models/gemini-pro-vision:generateContent?key=${AK.geminiApi}'),
// headers: {'Content-Type': 'application/json'},
// body: requestBody,
// );
// isloading = false;
// update();
// if (response.statusCode == 200) {
// var responseData = jsonDecode(response.body);
// // Process the responseData as needed
// var result =
// responseData['candidates'][0]['content']['parts'][0]['text'];
// RegExp regex = RegExp(r"```json([^`]*)```");
// String? jsonString =
// regex.firstMatch(responseData.toString())?.group(1)?.trim();
// if (jsonString != null) {
// // Convert the JSON object to a String
// jsonString = jsonEncode(json.decode(jsonString));
// responseMap = jsonDecode(jsonString);
// } else {
// }
// // Rest of your code...
// } else {
// }
// } else {
// }
// }
// Future<void> getCarLicenseJordanContent() async {
// await pickImage();
// isloading = true;
// update();
// if (image != null) {
// final imageBytes = await image.readAsBytes();
// final imageData = base64Encode(imageBytes);
// var requestBody = jsonEncode({
// 'contents': [
// {
// 'parts': [
// {
// 'inlineData': {
// 'mimeType': 'image/jpeg',
// 'data': imageData,
// },
// },
// {
// 'text':
// '''Extract the following information from the front face of the Jordanian ID card:
// Name
// National ID number
// Gender
// Date of birth
// Output the extracted information in the following JSON format''',
// },
// ],
// },
// ],
// 'generationConfig': {
// 'temperature': 0.4,
// 'topK': 32,
// 'topP': 1,
// 'maxOutputTokens': 4096,
// 'stopSequences': [],
// },
// 'safetySettings': [
// {
// 'category': 'HARM_CATEGORY_HARASSMENT',
// 'threshold': 'BLOCK_MEDIUM_AND_ABOVE',
// },
// {
// 'category': 'HARM_CATEGORY_HATE_SPEECH',
// 'threshold': 'BLOCK_MEDIUM_AND_ABOVE',
// },
// {
// 'category': 'HARM_CATEGORY_SEXUALLY_EXPLICIT',
// 'threshold': 'BLOCK_MEDIUM_AND_ABOVE',
// },
// {
// 'category': 'HARM_CATEGORY_DANGEROUS_CONTENT',
// 'threshold': 'BLOCK_MEDIUM_AND_ABOVE',
// },
// ],
// });
// final response = await http.post(
// Uri.parse(
// // 'https://${API_ENDPOINT}/v1/projects/${PROJECT_ID}/locations/${LOCATION_ID}/publishers/google/models/${MODEL_ID}:streamGenerateContent'),
// 'https://generativelanguage.googleapis.com/v1beta/models/gemini-pro-vision:generateContent?key=${AK.geminiApi}'),
// headers: {'Content-Type': 'application/json'},
// body: requestBody,
// );
// isloading = false;
// update();
// if (response.statusCode == 200) {
// var responseData = jsonDecode(response.body);
// // Process the responseData as needed
// var result =
// responseData['candidates'][0]['content']['parts'][0]['text'];
// RegExp regex = RegExp(r"```json([^`]*)```");
// String? jsonString =
// regex.firstMatch(responseData.toString())?.group(1)?.trim();
// if (jsonString != null) {
// // Convert the JSON object to a String
// jsonString = jsonEncode(json.decode(jsonString));
// responseCarLicenseMap = jsonDecode(jsonString);
// } else {
// }
// // Rest of your code...
// } else {
// }
// } else {
// }
// }
// Future<void> jordanID() async {
// await pickImage();
// isloading = true;
// update();
// if (image != null) {
// final imageBytes = await image.readAsBytes();
// final imageData = base64Encode(imageBytes);
// var requestBody = jsonEncode({
// 'contents': [
// {
// 'parts': [
// {
// 'inlineData': {
// 'mimeType': 'image/jpeg',
// 'data': imageData,
// },
// },
// {
// 'text':
// '''Extract the following information from the front face of the Jordanian ID card:
// Name
// National ID number
// Gender
// Date of birth
// Output the extracted information in the following JSON format''',
// },
// ],
// },
// ],
// 'generationConfig': {
// 'temperature': 0.4,
// 'topK': 32,
// 'topP': 1,
// 'maxOutputTokens': 4096,
// 'stopSequences': [],
// },
// 'safetySettings': [
// {
// 'category': 'HARM_CATEGORY_HARASSMENT',
// 'threshold': 'BLOCK_MEDIUM_AND_ABOVE',
// },
// {
// 'category': 'HARM_CATEGORY_HATE_SPEECH',
// 'threshold': 'BLOCK_MEDIUM_AND_ABOVE',
// },
// {
// 'category': 'HARM_CATEGORY_SEXUALLY_EXPLICIT',
// 'threshold': 'BLOCK_MEDIUM_AND_ABOVE',
// },
// {
// 'category': 'HARM_CATEGORY_DANGEROUS_CONTENT',
// 'threshold': 'BLOCK_MEDIUM_AND_ABOVE',
// },
// ],
// });
// final response = await http.post(
// Uri.parse(
// 'https://generativelanguage.googleapis.com/v1beta/models/gemini-pro-vision:generateContent?key=${AK.geminiApi}'),
// headers: {'Content-Type': 'application/json'},
// body: requestBody,
// );
// isloading = false;
// update();
// if (response.statusCode == 200) {
// var responseData = jsonDecode(response.body);
// // Process the responseData as needed
// var result =
// responseData['candidates'][0]['content']['parts'][0]['text'];
// RegExp regex = RegExp(r"```json([^`]*)```");
// String? jsonString =
// regex.firstMatch(responseData.toString())?.group(1)?.trim();
// if (jsonString != null) {
// // Convert the JSON object to a String
// jsonString = jsonEncode(json.decode(jsonString));
// responseCarLicenseMap = jsonDecode(jsonString);
// } else {
// }
// // Rest of your code...
// } else {
// }
// } else {
// }
// }
// Future<void> carLicenseJordan() async {
// await pickImage();
// isloading = true;
// update();
// if (image != null) {
// final imageBytes = await image.readAsBytes();
// final imageData = base64Encode(imageBytes);
// var requestBody = jsonEncode({
// 'contents': [
// {
// 'parts': [
// {
// 'inlineData': {
// 'mimeType': 'image/jpeg',
// 'data': imageData,
// },
// },
// {
// 'text':
// '''Extract the following information from the front face of the car license card in Jordan:
// * name
// * Address
// * Vehicle type
// * car_kind
// * car_color
// * Vehicle category
// * car_year
// * car_plate
// * Registration type
// * Usage type
// * expire_date_of_license
// Output the extracted information in the following JSON formate and make date format like YYYY-MM-DD''',
// },
// ],
// },
// ],
// 'generationConfig': {
// 'temperature': 0.4,
// 'topK': 32,
// 'topP': 1,
// 'maxOutputTokens': 4096,
// 'stopSequences': [],
// },
// 'safetySettings': [
// {
// 'category': 'HARM_CATEGORY_HARASSMENT',
// 'threshold': 'BLOCK_MEDIUM_AND_ABOVE',
// },
// {
// 'category': 'HARM_CATEGORY_HATE_SPEECH',
// 'threshold': 'BLOCK_MEDIUM_AND_ABOVE',
// },
// {
// 'category': 'HARM_CATEGORY_SEXUALLY_EXPLICIT',
// 'threshold': 'BLOCK_MEDIUM_AND_ABOVE',
// },
// {
// 'category': 'HARM_CATEGORY_DANGEROUS_CONTENT',
// 'threshold': 'BLOCK_MEDIUM_AND_ABOVE',
// },
// ],
// });
// final response = await http.post(
// Uri.parse(
// 'https://generativelanguage.googleapis.com/v1beta/models/gemini-1.0-pro-vision-latest:generateContent?key=${AK.geminiApi}'),
// headers: {'Content-Type': 'application/json'},
// body: requestBody,
// );
// isloading = false;
// update();
// if (response.statusCode == 200) {
// var responseData = jsonDecode(response.body);
// // Process the responseData as needed
// var result =
// responseData['candidates'][0]['content']['parts'][0]['text'];
// RegExp regex = RegExp(r"```json([^`]*)```");
// String? jsonString =
// regex.firstMatch(responseData.toString())?.group(1)?.trim();
// if (jsonString != null) {
// // Convert the JSON object to a String
// jsonString = jsonEncode(json.decode(jsonString));
// responseCarLicenseMap = jsonDecode(jsonString);
// } else {
// }
// // Rest of your code...
// } else {
// }
// } else {
// }
// }
// Future getTextFromCard(String prompt) async {
// await pickImage();
// isloading = true;
// update();
// if (image != null) {
// final imageBytes = await image.readAsBytes();
// final imageData = base64Encode(imageBytes);
// var requestBody = jsonEncode({
// 'contents': [
// {
// 'parts': [
// {
// 'inlineData': {
// 'mimeType': 'image/jpeg',
// 'data': imageData,
// },
// },
// {
// 'text': prompt,
// },
// ],
// },
// ],
// 'generationConfig': {
// "temperature": 1,
// "topK": 32,
// "topP": 0.1,
// "maxOutputTokens": 4096,
// "stopSequences": []
// },
// 'safetySettings': [
// {
// 'category': 'HARM_CATEGORY_HARASSMENT',
// 'threshold': 'BLOCK_MEDIUM_AND_ABOVE',
// },
// {
// 'category': 'HARM_CATEGORY_HATE_SPEECH',
// 'threshold': 'BLOCK_MEDIUM_AND_ABOVE',
// },
// {
// 'category': 'HARM_CATEGORY_SEXUALLY_EXPLICIT',
// 'threshold': 'BLOCK_MEDIUM_AND_ABOVE',
// },
// {
// 'category': 'HARM_CATEGORY_DANGEROUS_CONTENT',
// 'threshold': 'BLOCK_MEDIUM_AND_ABOVE',
// },
// ],
// });
// final response = await http.post(
// Uri.parse(
// 'https://generativelanguage.googleapis.com/v1beta/models/gemini-1.0-pro-vision-latest:generateContent?key=${AK.geminiApi}'),
// headers: {'Content-Type': 'application/json'},
// body: requestBody,
// );
// isloading = false;
// update();
// if (response.statusCode == 200) {
// var responseData = jsonDecode(response.body);
// // Process the responseData as needed
// var result =
// responseData['candidates'][0]['content']['parts'][0]['text'];
// RegExp regex = RegExp(r"```json([^`]*)```");
// String? jsonString =
// regex.firstMatch(responseData.toString())?.group(1)?.trim();
// if (jsonString != null) {
// // Convert the JSON object to a String
// jsonString = jsonEncode(json.decode(jsonString));
// responseBackCarLicenseMap = jsonDecode(jsonString);
// } else {
// }
// // Rest of your code...
// } else {
// }
// } else {
// }
// }
// Future<void> generateBackCarLicenseJordanContent() async {
// await pickImage();
// isloading = true;
// update();
// if (image != null) {
// final imageBytes = await image.readAsBytes();
// final imageData = base64Encode(imageBytes);
// var requestBody = jsonEncode({
// 'contents': [
// {
// 'parts': [
// {
// 'inlineData': {
// 'mimeType': 'image/jpeg',
// 'data': imageData,
// },
// },
// {
// 'text':
// 'write json output from extracting car license back face for these key ,vin,fuelType,passengerType,curbWeight,insuranceCompany,policyNumber,notes,insuranceType and output it json .dont add data else this image',
// },
// ],
// },
// ],
// 'generationConfig': {
// 'temperature': 0.4,
// 'topK': 343,
// 'topP': 1,
// 'maxOutputTokens': 4096,
// 'stopSequences': [],
// },
// 'safetySettings': [
// {
// 'category': 'HARM_CATEGORY_HARASSMENT',
// 'threshold': 'BLOCK_MEDIUM_AND_ABOVE',
// },
// {
// 'category': 'HARM_CATEGORY_HATE_SPEECH',
// 'threshold': 'BLOCK_MEDIUM_AND_ABOVE',
// },
// {
// 'category': 'HARM_CATEGORY_SEXUALLY_EXPLICIT',
// 'threshold': 'BLOCK_MEDIUM_AND_ABOVE',
// },
// {
// 'category': 'HARM_CATEGORY_DANGEROUS_CONTENT',
// 'threshold': 'BLOCK_MEDIUM_AND_ABOVE',
// },
// ],
// });
// final response = await http.post(
// Uri.parse(
// 'https://generativelanguage.googleapis.com/v1beta/models/gemini-pro-vision:generateContent?key=${AK.geminiApi}'),
// headers: {'Content-Type': 'application/json'},
// body: requestBody,
// );
// isloading = false;
// update();
// if (response.statusCode == 200) {
// var responseData = jsonDecode(response.body);
// // Process the responseData as needed
// var result =
// responseData['candidates'][0]['content']['parts'][0]['text'];
// RegExp regex = RegExp(r"```json([^`]*)```");
// String? jsonString =
// regex.firstMatch(responseData.toString())?.group(1)?.trim();
// if (jsonString != null) {
// // Convert the JSON object to a String
// jsonString = jsonEncode(json.decode(jsonString));
// responseBackCarLicenseMap = jsonDecode(jsonString);
// } else {
// }
// // Rest of your code...
// } else {
// }
// } else {
// }
// }
// Future<void> getFromCarRegistration() async {
// await pickImage();
// if (image != null) {
// final imageBytes = await image.readAsBytes();
// final imageData = base64Encode(imageBytes);
// var requestBody = jsonEncode({
// 'contents': [
// {
// 'parts': [
// {
// 'inlineData': {
// 'mimeType': 'image/jpeg',
// 'data': imageData,
// },
// },
// {
// 'text':
// 'write output json from image for[ vin, make, model, year, expiration_date, color, owner, registration_date ],output json type ',
// },
// ],
// },
// ],
// 'generationConfig': {
// 'temperature': 0.4,
// 'topK': 32,
// 'topP': 1,
// 'maxOutputTokens': 4096,
// 'stopSequences': [],
// },
// 'safetySettings': [
// {
// 'category': 'HARM_CATEGORY_HARASSMENT',
// 'threshold': 'BLOCK_MEDIUM_AND_ABOVE',
// },
// {
// 'category': 'HARM_CATEGORY_HATE_SPEECH',
// 'threshold': 'BLOCK_MEDIUM_AND_ABOVE',
// },
// {
// 'category': 'HARM_CATEGORY_SEXUALLY_EXPLICIT',
// 'threshold': 'BLOCK_MEDIUM_AND_ABOVE',
// },
// {
// 'category': 'HARM_CATEGORY_DANGEROUS_CONTENT',
// 'threshold': 'BLOCK_MEDIUM_AND_ABOVE',
// },
// ],
// });
// final response = await http.post(
// Uri.parse(
// 'https://generativelanguage.googleapis.com/v1beta/models/gemini-pro-vision:generateContent?key=${AK.geminiApi}'),
// headers: {'Content-Type': 'application/json'},
// body: requestBody,
// );
// if (response.statusCode == 200) {
// var responseData = jsonDecode(response.body);
// // Process the responseData as needed
// var result =
// responseData['candidates'][0]['content']['parts'][0]['text'];
// RegExp regex = RegExp(r"```json([^`]*)```");
// String? jsonString =
// regex.firstMatch(responseData.toString())?.group(1)?.trim();
// if (jsonString != null) {
// // Convert the JSON object to a String
// jsonString = jsonEncode(json.decode(jsonString));
// } else {
// }
// // Rest of your code...
// } else {
// }
// } else {
// }
// }
// @override
// void onInit() {
// // generateContent();
// super.onInit();
// }
// }

View File

@@ -0,0 +1,71 @@
import 'package:url_launcher/url_launcher.dart';
import 'dart:io';
void showInBrowser(String url) async {
if (await canLaunchUrl(Uri.parse(url))) {
launchUrl(Uri.parse(url));
} else {}
}
Future<void> makePhoneCall(String phoneNumber) async {
final Uri launchUri = Uri(
scheme: 'tel',
path: phoneNumber,
);
await launchUrl(launchUri);
}
void launchCommunication(
String method, String contactInfo, String message) async {
String url;
if (Platform.isIOS) {
switch (method) {
case 'phone':
url = 'tel:$contactInfo';
break;
case 'sms':
url = 'sms:$contactInfo?body=$message';
break;
case 'whatsapp':
url = 'https://api.whatsapp.com/send?phone=$contactInfo&text=$message';
break;
case 'email':
url = 'mailto:$contactInfo?subject=Subject&body=$message';
break;
default:
return;
}
} else if (Platform.isAndroid) {
switch (method) {
case 'phone':
url = 'tel:$contactInfo';
break;
case 'sms':
url = 'sms:$contactInfo?body=$message';
break;
case 'whatsapp':
url = 'whatsapp://send?phone=$contactInfo&text=$message';
break;
case 'email':
url = 'mailto:$contactInfo?subject=Subject&body=$message';
break;
default:
return;
}
} else {
return;
}
if (await canLaunchUrl(Uri.parse(url))) {
launchUrl(Uri.parse(url));
} else {}
}

View File

@@ -0,0 +1,36 @@
import 'dart:convert';
import '../../constant/links.dart';
import 'crud.dart';
class LlamaAi {
Future<Map> getCarRegistrationData(String input, prompt) async {
Map exrtatDataFinal = {};
String oneLine = input.replaceAll('\n', ' ');
// var res = await CRUD().getLlama(link: AppLink.gemini, payload: oneLine);
var res = await CRUD()
.getLlama(link: AppLink.llama, payload: oneLine, prompt: prompt);
var decod = jsonDecode(res.toString());
// exrtatDataFinal = jsonDecode(extractDataFromJsonString(decod['choices']));
extractDataFromJsonString(decod['choices'][0]['message']['content']);
return exrtatDataFinal;
}
String extractDataFromJsonString(String jsonString) {
// Remove any leading or trailing whitespace from the string
jsonString = jsonString.trim();
// Extract the JSON substring from the given string
final startIndex = jsonString.indexOf('{');
final endIndex = jsonString.lastIndexOf('}');
final jsonSubstring = jsonString.substring(startIndex, endIndex + 1);
// Parse the JSON substring into a Map
final jsonData = jsonDecode(jsonSubstring);
// Return the extracted data
return jsonEncode(jsonData);
}
}

View File

@@ -0,0 +1,133 @@
// import 'dart:async';
// import 'package:get/get.dart';
// import 'package:google_maps_flutter/google_maps_flutter.dart';
// import 'package:location/location.dart';
// import '../../constant/box_name.dart';
// import '../../constant/links.dart';
// import '../../main.dart';
// import 'crud.dart';
// // LocationController.dart
// class LocationController extends GetxController {
// LocationData? _currentLocation;
// late Location location;
// bool isLoading = false;
// late double heading = 0;
// late double accuracy = 0;
// late double previousTime = 0;
// late double latitude;
// late double totalDistance = 0;
// late double longitude;
// late DateTime time;
// late double speed = 0;
// late double speedAccuracy = 0;
// late double headingAccuracy = 0;
// bool isActive = false;
// late LatLng myLocation;
// String totalPoints = '0';
// LocationData? get currentLocation => _currentLocation;
// Timer? _locationTimer;
// @override
// void onInit() async {
// super.onInit();
// location = Location();
// getLocation();
// // startLocationUpdates();
// }
// Future<void> startLocationUpdates() async {
// if (box.read(BoxName.driverID) != null) {
// _locationTimer =
// Timer.periodic(const Duration(seconds: 5), (timer) async {
// try {
// // if (isActive) {
// if (double.parse(totalPoints) > -300) {
// await getLocation();
// // if (box.read(BoxName.driverID) != null) {
// await CRUD()
// .post(link: AppLink.addCarsLocationByPassenger, payload: {
// 'driver_id': box.read(BoxName.driverID).toString(),
// 'latitude': myLocation.latitude.toString(),
// 'longitude': myLocation.longitude.toString(),
// 'heading': heading.toString(),
// 'speed': (speed * 3.6).toStringAsFixed(1),
// 'distance': totalDistance == 0
// ? '0'
// : totalDistance < 1
// ? totalDistance.toStringAsFixed(3)
// : totalDistance.toStringAsFixed(1),
// 'status': box.read(BoxName.statusDriverLocation).toString()
// });
// }
// } catch (e) {
// // Handle the error gracefully
// }
// });
// }
// }
// void stopLocationUpdates() {
// _locationTimer?.cancel();
// }
// Future<void> getLocation() async {
// // isLoading = true;
// // update();
// bool serviceEnabled;
// PermissionStatus permissionGranted;
// // Check if location services are enabled
// serviceEnabled = await location.serviceEnabled();
// if (!serviceEnabled) {
// serviceEnabled = await location.requestService();
// if (!serviceEnabled) {
// // Location services are still not enabled, handle the error
// return;
// }
// }
// // Check if the app has permission to access location
// permissionGranted = await location.hasPermission();
// if (permissionGranted == PermissionStatus.denied) {
// permissionGranted = await location.requestPermission();
// if (permissionGranted != PermissionStatus.granted) {
// // Location permission is still not granted, handle the error
// return;
// }
// }
// // Configure location accuracy
// // LocationAccuracy desiredAccuracy = LocationAccuracy.high;
// // Get the current location
// LocationData _locationData = await location.getLocation();
// myLocation =
// (_locationData.latitude != null && _locationData.longitude != null
// ? LatLng(_locationData.latitude!, _locationData.longitude!)
// : null)!;
// speed = _locationData.speed!;
// heading = _locationData.heading!;
// // isLoading = false;
// update();
// }
// double calculateDistanceInKmPerHour(
// double? startTime, double? endTime, double speedInMetersPerSecond) {
// // Calculate the time difference in hours
// double timeDifferenceInHours = (endTime! - startTime!) / 1000 / 3600;
// // Convert speed to kilometers per hour
// double speedInKmPerHour = speedInMetersPerSecond * 3.6;
// // Calculate the distance in kilometers
// double distanceInKilometers = speedInKmPerHour * timeDifferenceInHours;
// return distanceInKilometers;
// }
// }

View File

@@ -0,0 +1,16 @@
// import 'package:location/location.dart';
// import 'package:get/get.dart';
// class LocationPermissions {
// late Location location;
// Future locationPermissions() async {
// location = Location();
// var permissionStatus = await location.requestPermission();
// if (permissionStatus == PermissionStatus.denied) {
// // The user denied the location permission.
// Get.defaultDialog(title: 'GPS Required Allow !.'.tr, middleText: '');
// return null;
// }
// }
// }

View File

@@ -0,0 +1,181 @@
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../../constant/box_name.dart';
import '../../constant/colors.dart';
import '../../constant/links.dart';
import '../../constant/style.dart';
import '../../main.dart';
import '../../views/widgets/elevated_btn.dart';
import '../../views/widgets/my_textField.dart';
import 'crud.dart';
class LogOutController extends GetxController {
TextEditingController checkTxtController = TextEditingController();
final formKey = GlobalKey<FormState>();
final formKey1 = GlobalKey<FormState>();
final emailTextController = TextEditingController();
Future deleteMyAccountDriver(String id) async {
await CRUD().post(link: AppLink.removeUser, payload: {'id': id}).then(
(value) => Get.snackbar('Deleted'.tr, 'Your Account is Deleted',
backgroundColor: AppColor.redColor));
}
checkBeforeDelete() async {
var res = await CRUD().post(
link: AppLink.deletecaptainAccounr,
payload: {'id': box.read(BoxName.driverID)}).then((value) => exit(0));
}
deletecaptainAccount() {
Get.defaultDialog(
backgroundColor: AppColor.yellowColor,
title: 'Are you sure to delete your account?'.tr,
middleText:
'Your data will be erased after 2 weeks\nAnd you will can\'t return to use app after 1 month ',
titleStyle: AppStyle.title,
content: Column(
children: [
Container(
width: Get.width,
decoration: AppStyle.boxDecoration,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
'Your data will be erased after 2 weeks\nAnd you will can\'t return to use app after 1 month'
.tr,
style: AppStyle.title.copyWith(color: AppColor.redColor),
),
),
),
const SizedBox(
height: 20,
),
Form(
key: formKey,
child: SizedBox(
width: Get.width,
child: MyTextForm(
controller: checkTxtController,
label: 'Enter Your First Name'.tr,
hint: 'Enter Your First Name'.tr,
type: TextInputType.name,
),
))
],
),
confirm: MyElevatedButton(
title: 'Delete'.tr,
onPressed: () {
if (checkTxtController.text == box.read(BoxName.nameDriver)) {
deletecaptainAccount();
}
}));
}
Future logOutPassenger() async {
Get.defaultDialog(
title: 'Are you Sure to LogOut?'.tr,
content: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
MyElevatedButton(
title: 'Cancel'.tr,
onPressed: () => Get.back(),
),
ElevatedButton(
style: ButtonStyle(
backgroundColor: MaterialStateProperty.all(AppColor.redColor),
),
onPressed: () {
// box.remove(BoxName.agreeTerms);
box.remove(BoxName.driverID);
box.remove(BoxName.email);
box.remove(BoxName.lang);
box.remove(BoxName.name);
box.remove(BoxName.passengerID);
box.remove(BoxName.phone);
box.remove(BoxName.tokenFCM);
box.remove(BoxName.tokens);
box.remove(BoxName.addHome);
box.remove(BoxName.addWork);
box.remove(BoxName.agreeTerms);
box.remove(BoxName.apiKeyRun);
box.remove(BoxName.countryCode);
box.remove(BoxName.passengerWalletTotal);
Get.offAll(const MainApp());
},
child: Text(
'Sign Out'.tr,
style:
AppStyle.title.copyWith(color: AppColor.secondaryColor),
))
],
));
}
Future logOutCaptain() async {
Get.defaultDialog(
title: 'Are you Sure to LogOut?'.tr,
titleStyle: AppStyle.title,
content: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
MyElevatedButton(
title: 'Cancel'.tr,
onPressed: () => Get.back(),
),
ElevatedButton(
style: ButtonStyle(
backgroundColor: MaterialStateProperty.all(AppColor.redColor),
),
onPressed: () {
// box.remove(BoxName.agreeTerms);
box.remove(BoxName.driverID);
box.remove(BoxName.sexDriver);
box.remove(BoxName.dobDriver);
box.remove(BoxName.nameDriver);
box.remove(BoxName.emailDriver);
box.remove(BoxName.phoneDriver);
box.remove(BoxName.statusDriverLocation);
box.remove(BoxName.cvvCodeDriver);
box.remove(BoxName.lastNameDriver);
box.remove(BoxName.passwordDriver);
box.remove(BoxName.cardNumberDriver);
box.remove(BoxName.expiryDateDriver);
box.remove(BoxName.cardHolderNameDriver);
box.remove(BoxName.vin);
box.remove(BoxName.make);
box.remove(BoxName.year);
box.remove(BoxName.owner);
box.remove(BoxName.onBoarding);
box.remove(BoxName.agreeTerms);
Get.offAll(const MainApp());
},
child: Text(
'Sign Out'.tr,
style:
AppStyle.title.copyWith(color: AppColor.secondaryColor),
))
],
));
}
deletePassengerAccount() async {
if (formKey1.currentState!.validate()) {
if (box.read(BoxName.email).toString() == emailTextController.text) {
await CRUD().post(link: AppLink.passengerRemovedAccountEmail, payload: {
'email': box.read(BoxName.email),
});
} else {
Get.snackbar('Email Wrong'.tr, 'Email you inserted is Wrong.'.tr,
snackPosition: SnackPosition.BOTTOM,
backgroundColor: AppColor.redColor);
}
}
}
}

View File

@@ -0,0 +1,25 @@
// import 'package:credit_card_scanner/credit_card_scanner.dart';
// import 'package:get/get.dart';
//
// class ScanIdCard extends GetxController {
// CardDetails? _cardDetails;
// CardScanOptions scanOptions = const CardScanOptions(
// scanCardHolderName: true,
// enableDebugLogs: true,
// validCardsToScanBeforeFinishingScan: 5,
// possibleCardHolderNamePositions: [
// CardHolderNameScanPosition.aboveCardNumber,
// ],
// );
//
// Future<void> scanCard() async {
// final CardDetails? cardDetails =
// await CardScanner.scanCard(scanOptions: scanOptions);
// if (cardDetails == null) {
// return;
// }
//
// _cardDetails = cardDetails;
// update();
// }
// }

View File

@@ -0,0 +1,14 @@
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
class SecureStorage {
final FlutterSecureStorage _storage = const FlutterSecureStorage();
void saveData(String key, value) async {
await _storage.write(key: key, value: value);
}
Future<String?> readData(String boxName) async {
final String? value = await _storage.read(key: boxName);
return value;
}
}

View File

@@ -0,0 +1,50 @@
import 'dart:async';
import 'dart:io';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:get/get.dart';
class SecurityChecks {
static const platform = MethodChannel(
'com.intaleq.intaleq_admin/security'); // Choose a unique channel name
static Future<bool> isDeviceCompromised() async {
try {
final bool result = await platform
.invokeMethod('isNativeRooted'); // Invoke the native method
return result;
} on PlatformException catch (e) {
print("Failed to check security status: ${e.message}");
return true; // Treat platform errors as a compromised device (for safety)
}
}
static isDeviceRootedFromNative(BuildContext context) async {
bool compromised = await isDeviceCompromised();
if (compromised) {
showDialog(
barrierDismissible: false,
context: context,
builder: (context) => AlertDialog(
title: Text("Security Warning".tr),
content: Text(
"Your device appears to be compromised. The app will now close."
.tr),
actions: [
TextButton(
onPressed: () {
SystemNavigator.pop(); // Close the app
},
child: Text("OK"),
),
],
),
);
} else {
// Continue with normal app flow
print("Device is secure.");
}
}
}

View File

@@ -0,0 +1,455 @@
import 'dart:convert';
import 'dart:io';
import 'package:flutter_image_compress/flutter_image_compress.dart';
import 'package:get/get.dart';
import 'package:http/http.dart' as http;
import 'package:image_cropper/image_cropper.dart';
import 'package:image_picker/image_picker.dart';
import 'package:path/path.dart';
import 'package:image/image.dart' as img;
import 'package:path_provider/path_provider.dart' as path_provider;
import '../../constant/api_key.dart';
import '../../constant/box_name.dart';
import '../../constant/colors.dart';
import '../../constant/info.dart';
import '../../main.dart';
import '../../print.dart';
import 'encrypt_decrypt.dart';
class ImageController extends GetxController {
File? myImage;
bool isloading = false;
CroppedFile? croppedFile;
final picker = ImagePicker();
var image;
Future<img.Image> detectAndCropDocument(File imageFile) async {
img.Image? image = img.decodeImage(await imageFile.readAsBytes());
if (image == null) throw Exception('Unable to decode image');
int left = image.width, top = image.height, right = 0, bottom = 0;
// Threshold for considering a pixel as part of the document (adjust as needed)
const int threshold = 240;
for (int y = 0; y < image.height; y++) {
for (int x = 0; x < image.width; x++) {
final pixel = image.getPixel(x, y);
final luminance = img.getLuminance(pixel);
if (luminance < threshold) {
left = x < left ? x : left;
top = y < top ? y : top;
right = x > right ? x : right;
bottom = y > bottom ? y : bottom;
}
}
}
// Add a small padding
left = (left - 5).clamp(0, image.width);
top = (top - 5).clamp(0, image.height);
right = (right + 5).clamp(0, image.width);
bottom = (bottom + 5).clamp(0, image.height);
return img.copyCrop(image,
x: left, y: top, width: right - left, height: bottom - top);
}
Future<File> rotateImageIfNeeded(File imageFile) async {
img.Image croppedDoc = await detectAndCropDocument(imageFile);
// Check if the document is in portrait orientation
bool isPortrait = croppedDoc.height > croppedDoc.width;
img.Image processedImage;
if (isPortrait) {
// Rotate the image by 90 degrees clockwise
processedImage = img.copyRotate(croppedDoc, angle: 90);
} else {
processedImage = croppedDoc;
}
// Get temporary directory
final tempDir = await path_provider.getTemporaryDirectory();
final tempPath = tempDir.path;
// Create the processed image file
File processedFile = File('$tempPath/processed_image.jpg');
await processedFile.writeAsBytes(img.encodeJpg(processedImage));
return processedFile;
}
Future<File> rotateImage(File imageFile) async {
// Read the image file
img.Image? image = img.decodeImage(await imageFile.readAsBytes());
if (image == null) return imageFile;
// Rotate the image by 90 degrees clockwise
img.Image rotatedImage = img.copyRotate(image, angle: 90);
// Get temporary directory
final tempDir = await path_provider.getTemporaryDirectory();
final tempPath = tempDir.path;
// Create the rotated image file
File rotatedFile = File('$tempPath/rotated_image.jpg');
await rotatedFile.writeAsBytes(img.encodeJpg(rotatedImage));
return rotatedFile;
}
choosImage(String link, String imageType, String id) async {
try {
final pickedImage = await picker.pickImage(
source: ImageSource.camera,
preferredCameraDevice: CameraDevice.rear,
);
if (pickedImage == null) return;
image = File(pickedImage.path);
croppedFile = await ImageCropper().cropImage(
sourcePath: image!.path,
uiSettings: [
AndroidUiSettings(
toolbarTitle: 'Cropper'.tr,
toolbarColor: AppColor.blueColor,
toolbarWidgetColor: AppColor.yellowColor,
initAspectRatio: CropAspectRatioPreset.original,
lockAspectRatio: false,
),
IOSUiSettings(
title: 'Cropper'.tr,
),
],
);
if (croppedFile == null) return;
myImage = File(croppedFile!.path);
isloading = true;
update();
// Rotate the compressed image
File processedImage = await rotateImageIfNeeded(File(croppedFile!.path));
File compressedImage = await compressImage(processedImage);
print('link =$link');
Log.print('link: ${link}');
await uploadImage(
compressedImage,
{
'driverID': id,
'imageType': imageType,
},
link,
);
} catch (e) {
print('Error in choosImage: $e');
Get.snackbar('Image Upload Failed'.tr, e.toString(),
backgroundColor: AppColor.redColor);
} finally {
isloading = false;
update();
}
}
// choosFaceFromDriverLicense(String link, String imageType) async {
// final pickedImage = await picker.pickImage(
// source: ImageSource.camera,
// preferredCameraDevice: CameraDevice.rear,
// );
// if (pickedImage == null) return;
// image = File(pickedImage.path);
// File? processedImage;
// // For face images, use face detection and cropping
// processedImage = await detectAndCropFace(image!);
// if (processedImage == null) {
// Get.snackbar('Face Detection Failed', 'No face detected in the image.');
// return;
// }
// isloading = true;
// update();
// File compressedImage = await compressImage(processedImage);
// try {
// await uploadImage(
// compressedImage,
// {
// 'driverID': box.read(BoxName.driverID).toString(),
// 'imageType': imageType
// },
// link,
// );
// } catch (e) {
// Get.snackbar('Image Upload Failed'.tr, e.toString(),
// backgroundColor: AppColor.redColor);
// } finally {
// isloading = false;
// update();
// }
// }
choosFace(String link, String imageType) async {
final pickedImage = await picker.pickImage(
source: ImageSource.camera,
preferredCameraDevice: CameraDevice.front,
);
if (pickedImage != null) {
image = File(pickedImage.path);
isloading = true;
update();
// Compress the image
File compressedImage = await compressImage(File(pickedImage.path));
// Save the picked image directly
// File savedImage = File(pickedImage.path);
print('link =$link');
try {
await uploadImage(
compressedImage,
{
'driverID':
box.read(BoxName.driverID) ?? box.read(BoxName.passengerID),
'imageType': imageType
},
link,
);
} catch (e) {
Get.snackbar('Image Upload Failed'.tr, e.toString(),
backgroundColor: AppColor.redColor);
} finally {
isloading = false;
update();
}
}
}
uploadImage(File file, Map data, String link) async {
var request = http.MultipartRequest(
'POST',
Uri.parse(link),
);
Log.print('request: ${request}');
var length = await file.length();
var stream = http.ByteStream(file.openRead());
final headers = {
'Authorization':
'Bearer ${r(box.read(BoxName.jwt)).split(AppInformation.addd)[0]}',
// 'X-HMAC-Auth': '${box.read(BoxName.hmac)}',
};
Log.print('headers: ${headers}');
var multipartFile = http.MultipartFile(
'image',
stream,
length,
filename: basename(file.path),
);
request.headers.addAll(headers);
// Set the file name to the driverID
request.files.add(
http.MultipartFile(
'image',
stream,
length,
filename: '${box.read(BoxName.driverID)}.jpg',
),
);
data.forEach((key, value) {
request.fields[key] = value;
});
var myrequest = await request.send();
var res = await http.Response.fromStream(myrequest);
if (res.statusCode == 200) {
Log.print('jsonDecode(res.body): ${jsonDecode(res.body)}');
if (jsonDecode(res.body)['status'] == 'Image uploaded successfully!') {
Get.snackbar('Success'.tr, 'Image uploaded successfully!'.tr,
backgroundColor: AppColor.greenColor);
}
return jsonDecode(res.body);
} else {
throw Exception(
'Failed to upload image: ${res.statusCode} - ${res.body}');
}
}
choosImagePicture(String link, String imageType) async {
final pickedImage = await picker.pickImage(
source: ImageSource.gallery,
// preferredCameraDevice: CameraDevice.rear,
// maxHeight: Get.height * .3,
// maxWidth: Get.width * .9,
// imageQuality: 100,
);
image = File(pickedImage!.path);
croppedFile = await ImageCropper().cropImage(
sourcePath: image!.path,
uiSettings: [
AndroidUiSettings(
toolbarTitle: 'Cropper'.tr,
toolbarColor: AppColor.blueColor,
toolbarWidgetColor: AppColor.yellowColor,
initAspectRatio: CropAspectRatioPreset.original,
lockAspectRatio: false),
IOSUiSettings(
title: 'Cropper'.tr,
),
],
);
myImage = File(pickedImage.path);
isloading = true;
update();
// Save the cropped image
// File savedCroppedImage = File(croppedFile!.path);
File compressedImage = await compressImage(File(croppedFile!.path));
print('link =$link');
try {
await uploadImage(
compressedImage,
{
'driverID':
box.read(BoxName.driverID) ?? box.read(BoxName.passengerID),
'imageType': imageType
},
link,
);
} catch (e) {
Get.snackbar('Image Upload Failed'.tr, e.toString(),
backgroundColor: AppColor.redColor);
} finally {
isloading = false;
update();
}
}
uploadImagePicture(File file, Map data, String link) async {
var request = http.MultipartRequest(
'POST',
Uri.parse(link), //'https://ride.mobile-app.store/uploadImage1.php'
);
var length = await file.length();
var stream = http.ByteStream(file.openRead());
var multipartFile = http.MultipartFile(
'image',
stream,
length,
filename: basename(file.path),
);
request.headers.addAll({
'Authorization':
'Basic ${base64Encode(utf8.encode(AK.basicAuthCredentials.toString()))}',
});
// Set the file name to the driverID
request.files.add(
http.MultipartFile(
'image',
stream,
length,
filename: '${box.read(BoxName.driverID)}.jpg',
),
);
data.forEach((key, value) {
request.fields[key] = value;
});
var myrequest = await request.send();
var res = await http.Response.fromStream(myrequest);
if (res.statusCode == 200) {
return jsonDecode(res.body);
} else {
throw Exception(
'Failed to upload image: ${res.statusCode} - ${res.body}');
}
}
}
Future<File> compressImage(File file) async {
final dir = await path_provider.getTemporaryDirectory();
final targetPath = "${dir.absolute.path}/temp.jpg";
var result = await FlutterImageCompress.compressAndGetFile(
file.absolute.path,
targetPath,
quality: 70,
minWidth: 1024,
minHeight: 1024,
);
return File(result!.path);
}
// Future<File> detectAndCropFace(File imageFile) async {
// final inputImage = InputImage.fromFilePath(imageFile.path);
// final options = FaceDetectorOptions(
// enableClassification: false,
// enableLandmarks: false,
// enableTracking: false,
// minFaceSize: 0.15,
// performanceMode: FaceDetectorMode.accurate,
// );
// final faceDetector = FaceDetector(options: options);
// try {
// final List<Face> faces = await faceDetector.processImage(inputImage);
// final image = img.decodeImage(await imageFile.readAsBytes());
// if (image == null) throw Exception('Unable to decode image');
// int left, top, width, height;
// if (faces.isNotEmpty) {
// // Face detected, crop around the face
// final face = faces[0];
// double padding = 0.2; // 20% padding
// int paddingX = (face.boundingBox.width * padding).round();
// int paddingY = (face.boundingBox.height * padding).round();
// left = (face.boundingBox.left - paddingX).round();
// top = (face.boundingBox.top - paddingY).round();
// width = (face.boundingBox.width + 2 * paddingX).round();
// height = (face.boundingBox.height + 2 * paddingY).round();
// } else {
// // No face detected, crop the center of the image
// int size = min(image.width, image.height);
// left = (image.width - size) ~/ 2;
// top = (image.height - size) ~/ 2;
// width = size;
// height = size;
// }
// // Ensure dimensions are within image bounds
// left = left.clamp(0, image.width - 1);
// top = top.clamp(0, image.height - 1);
// width = width.clamp(1, image.width - left);
// height = height.clamp(1, image.height - top);
// final croppedImage =
// img.copyCrop(image, x: left, y: top, width: width, height: height);
// // Save the cropped image
// final tempDir = await path_provider.getTemporaryDirectory();
// final tempPath = tempDir.path;
// final croppedFile = File('$tempPath/cropped_image.jpg');
// await croppedFile.writeAsBytes(img.encodeJpg(croppedImage, quality: 100));
// return croppedFile;
// } finally {
// faceDetector.close();
// }
// }

View File

@@ -0,0 +1,108 @@
// import 'dart:convert';
// import 'dart:io';
// import 'package:get/get.dart';
// import 'package:http/http.dart' as http;
// import 'package:image_cropper/image_cropper.dart';
// import 'package:image_picker/image_picker.dart';
// import 'package:path/path.dart';
// import '../../constant/api_key.dart';
// import '../../constant/box_name.dart';
// import '../../constant/colors.dart';
// import '../../main.dart';
// class ImageController extends GetxController {
// File? myImage;
// bool isloading = false;
// CroppedFile? croppedFile;
// final picker = ImagePicker();
// var image;
// choosImage(String link, String imageType) async {
// final pickedImage = await picker.pickImage(source: ImageSource.gallery);
// image = File(pickedImage!.path);
// croppedFile = await ImageCropper().cropImage(
// sourcePath: image!.path,
// aspectRatioPresets: [
// CropAspectRatioPreset.square,
// CropAspectRatioPreset.ratio3x2,
// CropAspectRatioPreset.original,
// CropAspectRatioPreset.ratio4x3,
// CropAspectRatioPreset.ratio16x9
// ],
// uiSettings: [
// AndroidUiSettings(
// toolbarTitle: 'Cropper'.tr,
// toolbarColor: AppColor.blueColor,
// toolbarWidgetColor: AppColor.yellowColor,
// initAspectRatio: CropAspectRatioPreset.original,
// lockAspectRatio: false),
// IOSUiSettings(
// title: 'Cropper'.tr,
// ),
// ],
// );
// myImage = File(pickedImage.path);
// isloading = true;
// update();
// // Save the cropped image
// File savedCroppedImage = File(croppedFile!.path);
// try {
// await uploadImage(
// savedCroppedImage,
// {
// 'driverID':
// box.read(BoxName.driverID) ?? box.read(BoxName.passengerID),
// 'imageType': imageType
// },
// link,
// );
// } catch (e) {
// Get.snackbar('Image Upload Failed'.tr, e.toString(),
// backgroundColor: AppColor.redColor);
// } finally {
// isloading = false;
// update();
// }
// }
// uploadImage(File file, Map data, String link) async {
// var request = http.MultipartRequest(
// 'POST',
// Uri.parse(link), //'https://ride.mobile-app.store/uploadImage1.php'
// );
// var length = await file.length();
// var stream = http.ByteStream(file.openRead());
// var multipartFile = http.MultipartFile(
// 'image',
// stream,
// length,
// filename: basename(file.path),
// );
// request.headers.addAll({
// 'Authorization':
// 'Basic ${base64Encode(utf8.encode(AK.basicAuthCredentials.toString()))}',
// });
// // Set the file name to the driverID
// request.files.add(
// http.MultipartFile(
// 'image',
// stream,
// length,
// filename: '${box.read(BoxName.driverID)}.jpg',
// ),
// );
// data.forEach((key, value) {
// request.fields[key] = value;
// });
// var myrequest = await request.send();
// var res = await http.Response.fromStream(myrequest);
// if (res.statusCode == 200) {
// return jsonDecode(res.body);
// } else {
// throw Exception(
// 'Failed to upload image: ${res.statusCode} - ${res.body}');
// }
// }
// }

View File

@@ -0,0 +1,96 @@
import 'dart:convert';
import 'package:get/get.dart';
import 'package:siro_admin/constant/colors.dart';
import '../../constant/links.dart';
import '../firebase/firbase_messge.dart';
import 'crud.dart';
class WalletController extends GetxController {
String paymentToken = '';
Future<String> generateTokenDriver(String amount, driverID) async {
var res = await CRUD().post(link: AppLink.addPaymentTokenDriver, payload: {
'driverID': driverID.toString(),
'amount': amount.toString(),
});
var d = jsonDecode(res);
return d['message'];
}
addPaymentToDriver(String amount, driverID, token) async {
paymentToken = await generateTokenDriver(amount.toString(), driverID);
var res = await CRUD().post(link: AppLink.addDrivePayment, payload: {
'rideId': 'gift_$driverID _${DateTime.now().toIso8601String()}',
'amount': amount,
'payment_method': 'visaRide',
'passengerID': 'gift',
'token': paymentToken,
'driverID': driverID.toString(),
});
if (res != 'failure') {
FirebaseMessagesController().sendNotificationToAnyWithoutData(
"لديك هدية من سفَر".tr,
'لقد حصلت على هدية من سفر بقيمة $amount ',
token, // Access token correctly
'ding.wav',
);
Get.snackbar('success', 'addPaymentToDriver',
backgroundColor: AppColor.greenColor);
} else {
Get.snackbar('error', 'addPaymentToDriver',
backgroundColor: AppColor.redColor);
}
}
Future addDriverWallet(String paymentMethod, driverID, point, phone) async {
// paymentToken = await generateToken(count);
// var paymentID = await getPaymentId(paymentMethod, point.toString());
await CRUD().postWallet(link: AppLink.addFromAdmin, payload: {
'driverID': driverID.toString(),
'paymentID': 'gift_connect_$driverID${DateTime.timestamp()}'.toString(),
'amount': point,
'token': 'gift_connect',
'paymentMethod': paymentMethod,
'phone': phone,
});
}
Future addDrivergift300(String paymentMethod, driverID, point, phone) async {
// paymentToken = await generateToken(count);
// var paymentID = await getPaymentId(paymentMethod, point.toString());
var res = await CRUD().postWallet(link: AppLink.add300ToDriver, payload: {
'driverID': driverID.toString(),
'paymentID': paymentMethod,
'amount': point,
'token': 'gift_connect_300',
'paymentMethod': paymentMethod,
'phone': phone,
});
if (res != 'failure') {
Get.snackbar('success', 'addDrivergift300',
backgroundColor: AppColor.greenColor);
} else {
Get.snackbar('error', res, backgroundColor: AppColor.redColor);
}
}
Future addSeferWallet(String point, driverID) async {
var amount = (int.parse(point) * -1).toStringAsFixed(0);
var seferToken = await generateTokenDriver(amount, driverID);
var res = await CRUD().post(link: AppLink.addSeferWallet, payload: {
'amount': amount.toString(),
'paymentMethod': 'visaRide',
'passengerId': 'gift$driverID',
'token': seferToken,
'driverId': driverID.toString(),
});
if (res != 'failure') {
Get.snackbar('success', 'addSeferWallet',
backgroundColor: AppColor.greenColor);
} else {
Get.snackbar('error', 'addSeferWallet',
backgroundColor: AppColor.redColor);
}
}
}

View File

@@ -0,0 +1,237 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'firebase/notification_service.dart';
class NotificationController extends GetxController {
final GlobalKey<FormState> formKey = GlobalKey<FormState>();
final TextEditingController titleController = TextEditingController();
final TextEditingController bodyController = TextEditingController();
// ألوان الثيم (متناسقة مع باقي الصفحات المحسنة)
final Color _dialogColor = const Color(0xFF1A1F3A);
final Color _inputColor = const Color(0xFF0A0E27);
final Color _primaryAccent = const Color(0xFF6366F1);
@override
void onClose() {
titleController.dispose();
bodyController.dispose();
super.onClose();
}
/// تنظيف الحقول
void _clearFields() {
titleController.clear();
bodyController.clear();
}
/// إرسال إشعار للسائقين
Future<void> sendNotificationDrivers() async {
_clearFields();
await _showCustomDialog(
dialogTitle: 'إشعار للسائقين',
targetAudience: 'drivers',
icon: Icons.drive_eta_rounded,
iconColor: Colors.orangeAccent,
);
}
/// إرسال إشعار للركاب
Future<void> sendNotificationPassengers() async {
_clearFields();
await _showCustomDialog(
dialogTitle: 'إشعار للركاب',
targetAudience: 'passengers',
icon: Icons.people_alt_rounded,
iconColor: Colors.blueAccent,
);
}
/// دالة عامة لإظهار نافذة الإرسال بتصميم عصري
Future<void> _showCustomDialog({
required String dialogTitle,
required String targetAudience,
required IconData icon,
required Color iconColor,
}) {
return Get.dialog(
Dialog(
backgroundColor: _dialogColor,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)),
elevation: 10,
child: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.all(24),
child: Form(
key: formKey,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
// 1. أيقونة العنوان
Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: iconColor.withOpacity(0.15),
shape: BoxShape.circle,
boxShadow: [
BoxShadow(
color: iconColor.withOpacity(0.1),
blurRadius: 10,
offset: const Offset(0, 4),
),
],
),
child: Icon(icon, color: iconColor, size: 32),
),
const SizedBox(height: 16),
// 2. العنوان
Text(
dialogTitle,
style: const TextStyle(
color: Colors.white,
fontSize: 18,
fontWeight: FontWeight.bold,
fontFamily: 'Segoe UI',
),
),
const SizedBox(height: 24),
// 3. حقول الإدخال
_buildModernTextField(
controller: titleController,
hint: 'عنوان الإشعار',
icon: Icons.title_rounded,
),
const SizedBox(height: 16),
_buildModernTextField(
controller: bodyController,
hint: 'نص الرسالة',
icon: Icons.message_rounded,
maxLines: 3,
),
const SizedBox(height: 32),
// 4. الأزرار
Row(
children: [
Expanded(
child: TextButton(
onPressed: () => Get.back(),
style: TextButton.styleFrom(
foregroundColor: Colors.white54,
padding: const EdgeInsets.symmetric(vertical: 12),
),
child: const Text('إلغاء'),
),
),
const SizedBox(width: 12),
Expanded(
child: ElevatedButton(
onPressed: () async {
if (titleController.text.trim().isEmpty ||
bodyController.text.trim().isEmpty) {
Get.snackbar(
"تنبيه",
"الرجاء تعبئة جميع الحقول",
backgroundColor: Colors.amber.withOpacity(0.8),
colorText: Colors.white,
snackPosition: SnackPosition.TOP,
);
return;
}
Get.back(); // إغلاق النافذة
await _processSending(targetAudience);
},
style: ElevatedButton.styleFrom(
backgroundColor: _primaryAccent,
foregroundColor: Colors.white,
padding: const EdgeInsets.symmetric(vertical: 12),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
elevation: 0,
),
child: const Text('إرسال'),
),
),
],
),
],
),
),
),
),
),
barrierColor: Colors.black.withOpacity(0.7), // تعتيم الخلفية
);
}
/// تنفيذ عملية الإرسال الفعلية
Future<void> _processSending(String target) async {
// إظهار تنبيه بدء العملية
Get.snackbar(
"جاري الإرسال",
"يتم إرسال الإشعار لـ $target...",
backgroundColor: _primaryAccent.withOpacity(0.2),
colorText: Colors.white,
duration: const Duration(seconds: 2),
);
try {
// استدعاء خدمة الإرسال
await NotificationService.sendNotification(
target: target,
title: titleController.text,
body: bodyController.text,
isTopic: true,
category: 'fromAdmin',
);
Get.snackbar(
"نجاح",
"تم إرسال الإشعار بنجاح",
backgroundColor: Colors.green.withOpacity(0.5),
colorText: Colors.white,
);
} catch (e) {
Get.snackbar(
"خطأ",
"فشل إرسال الإشعار: $e",
backgroundColor: Colors.red.withOpacity(0.5),
colorText: Colors.white,
);
}
}
/// تصميم حقل الإدخال المخصص (Dark Input Field)
Widget _buildModernTextField({
required TextEditingController controller,
required String hint,
required IconData icon,
int maxLines = 1,
}) {
return Container(
decoration: BoxDecoration(
color: _inputColor,
borderRadius: BorderRadius.circular(12),
border: Border.all(color: Colors.white.withOpacity(0.1)),
),
child: TextField(
controller: controller,
style: const TextStyle(color: Colors.white),
maxLines: maxLines,
decoration: InputDecoration(
hintText: hint,
hintStyle:
TextStyle(color: Colors.white.withOpacity(0.3), fontSize: 14),
prefixIcon: Icon(icon, color: Colors.white38, size: 20),
border: InputBorder.none,
contentPadding:
const EdgeInsets.symmetric(horizontal: 16, vertical: 14),
),
),
);
}
}

View File

@@ -0,0 +1,139 @@
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../../constant/links.dart';
import '../functions/crud.dart';
class RideLookupController extends GetxController {
final TextEditingController phoneCtrl = TextEditingController();
bool isLoading = false;
Map<String, dynamic>? passenger; // {id, first_name, last_name, phone}
Map<String, dynamic>? ride; // Ride details
// Status filter for the search tab
String currentStatusFilter = '';
// Whitelist of allowed statuses for the Update Dropdown
// UPDATED: Matches the exact types you requested
final List<String> statusOptions = const [
'Pending',
'Accepted',
'EnRoute',
'Arrived',
'Started',
'Completed',
'Canceled',
];
String? selectedStatus;
// Hydrate dropdown value from the current ride data
void hydrateSelectedFromRide() {
final cur = (ride?['status'] ?? '') as String;
selectedStatus = statusOptions.contains(cur) ? cur : null;
update();
}
Future<bool> updateRideStatus({String? note}) async {
if (ride == null) return false;
if (selectedStatus == null || selectedStatus!.isEmpty) return false;
isLoading = true;
update();
try {
final res = await CRUD().post(
link: AppLink.admin_update_ride_status,
payload: {
'id': "${ride!['id']}",
'status': selectedStatus!,
if (note != null && note.trim().isNotEmpty) 'reason': note.trim(),
},
);
final d = jsonDecode(res);
final ok = (d['status'] == 'success');
if (ok) {
// Update local ride details from response
final updated = (d['message'] ?? d)['ride'];
if (updated != null) {
ride = Map<String, dynamic>.from(updated);
}
update();
return true;
} else {
return false;
}
} catch (_) {
return false;
} finally {
isLoading = false;
update();
}
}
// Updated to accept status filter
Future<bool> searchLatest({String? status}) async {
final phone = phoneCtrl.text.trim();
// If status is passed, update the current filter
if (status != null) {
currentStatusFilter = status;
}
// If phone is empty, we stop unless your API supports fetching "latest of all users"
if (phone.isEmpty) {
return false;
}
isLoading = true;
update();
try {
final res = await CRUD().post(
link: AppLink.admin_get_rides_by_phone,
payload: {
'phone': phone,
// If filter is 'All', send empty string to PHP, otherwise send the exact status
'status': currentStatusFilter == 'All' ? '' : currentStatusFilter,
},
);
final d = res;
if (d['status'] == 'success') {
passenger = (d['message'] ?? d)['passenger'];
ride = (d['message'] ?? d)['ride'];
// Hydrate the dropdown for the update section based on the fetched ride
hydrateSelectedFromRide();
update();
return true;
} else {
passenger = null;
ride = null;
update();
return false;
}
} catch (_) {
passenger = null;
ride = null;
update();
return false;
} finally {
isLoading = false;
update();
}
}
String rideHeader() {
if (ride == null) return '';
final id = ride!['id'] ?? '';
final st = (ride!['status'] ?? '').toString();
return "Ride #$id$st";
}
}

View File

@@ -0,0 +1,150 @@
import 'dart:async';
import 'package:get/get.dart';
import 'package:siro_admin/constant/links.dart';
import '../../print.dart';
// --- Models ---
class ServerData {
final CpuInfo cpu;
final MemoryInfo memory;
final DiskInfo disk;
final Map<String, String> services;
final List<ProcessInfo> topProcesses;
final NetworkInfo network;
final UptimeInfo uptime;
final String timestamp;
ServerData({
required this.cpu,
required this.memory,
required this.disk,
required this.services,
required this.topProcesses,
required this.network,
required this.uptime,
required this.timestamp,
});
factory ServerData.fromJson(Map<String, dynamic> json) {
return ServerData(
cpu: CpuInfo.fromJson(json['cpu']),
memory: MemoryInfo.fromJson(json['memory']),
disk: DiskInfo.fromJson(json['disk']),
services: Map<String, String>.from(json['services']),
topProcesses: (json['top_processes'] as List)
.map((i) => ProcessInfo.fromJson(i))
.toList(),
network: NetworkInfo.fromJson(json['network']),
uptime: UptimeInfo.fromJson(json['uptime']),
timestamp: json['timestamp'],
);
}
}
class CpuInfo {
final double percent;
final int cores;
final double load1m;
CpuInfo({required this.percent, required this.cores, required this.load1m});
factory CpuInfo.fromJson(Map<String, dynamic> json) => CpuInfo(
percent: json['percent'].toDouble(),
cores: json['cores'],
load1m: json['load_1m'].toDouble());
}
class MemoryInfo {
final double percent;
final double usedGb;
final double totalGb;
MemoryInfo(
{required this.percent, required this.usedGb, required this.totalGb});
factory MemoryInfo.fromJson(Map<String, dynamic> json) => MemoryInfo(
percent: json['percent'].toDouble(),
usedGb: json['used_gb'].toDouble(),
totalGb: json['total_gb'].toDouble());
}
class DiskInfo {
final double percent;
final double usedGb;
final double totalGb;
DiskInfo(
{required this.percent, required this.usedGb, required this.totalGb});
factory DiskInfo.fromJson(Map<String, dynamic> json) => DiskInfo(
percent: json['percent'].toDouble(),
usedGb: json['used_gb'].toDouble(),
totalGb: json['total_gb'].toDouble());
}
class ProcessInfo {
final String name;
final String usage;
ProcessInfo({required this.name, required this.usage});
factory ProcessInfo.fromJson(Map<String, dynamic> json) =>
ProcessInfo(name: json['name'], usage: json['usage']);
}
class NetworkInfo {
final double receivedMb;
final double sentMb;
NetworkInfo({required this.receivedMb, required this.sentMb});
factory NetworkInfo.fromJson(Map<String, dynamic> json) => NetworkInfo(
receivedMb: json['received_mb'].toDouble(),
sentMb: json['sent_mb'].toDouble());
}
class UptimeInfo {
final String formatted;
UptimeInfo({required this.formatted});
factory UptimeInfo.fromJson(Map<String, dynamic> json) =>
UptimeInfo(formatted: json['formatted']);
}
// --- Controller ---
class ServerMonitorController extends GetxController {
var isLoading = false.obs;
var serverData = Rxn<ServerData>();
var errorMessage = ''.obs;
Timer? _timer; // تخزين التايمر
@override
void onInit() {
super.onInit();
fetchServerData();
// تحديث تلقائي كل 60 ثانية
_timer = Timer.periodic(Duration(seconds: 60), (_) {
fetchServerData();
});
}
@override
void onClose() {
// إلغاء التحديث عند إغلاق الصفحة
_timer?.cancel();
super.onClose();
}
Future<void> fetchServerData() async {
try {
isLoading(true);
errorMessage('');
final response = await GetConnect().get(AppLink.serverMonitor);
if (response.status.hasError) {
errorMessage.value = 'خطأ في الاتصال: ${response.statusText}';
} else {
serverData.value = ServerData.fromJson(response.body);
}
} catch (e) {
errorMessage.value = 'حدث خطأ: $e';
} finally {
isLoading(false);
}
}
}