first commit
This commit is contained in:
75
siro_admin/lib/controller/admin/analytics_v2_controller.dart
Normal file
75
siro_admin/lib/controller/admin/analytics_v2_controller.dart
Normal 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');
|
||||
}
|
||||
}
|
||||
}
|
||||
141
siro_admin/lib/controller/admin/captain_admin_controller.dart
Normal file
141
siro_admin/lib/controller/admin/captain_admin_controller.dart
Normal 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();
|
||||
}
|
||||
}
|
||||
70
siro_admin/lib/controller/admin/complaint_controller.dart
Normal file
70
siro_admin/lib/controller/admin/complaint_controller.dart
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
111
siro_admin/lib/controller/admin/dashboard_controller.dart
Normal file
111
siro_admin/lib/controller/admin/dashboard_controller.dart
Normal 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();
|
||||
}
|
||||
}
|
||||
62
siro_admin/lib/controller/admin/dashboard_v2_controller.dart
Normal file
62
siro_admin/lib/controller/admin/dashboard_v2_controller.dart
Normal 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');
|
||||
}
|
||||
}
|
||||
}
|
||||
97
siro_admin/lib/controller/admin/driver_docs_controller.dart
Normal file
97
siro_admin/lib/controller/admin/driver_docs_controller.dart
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
61
siro_admin/lib/controller/admin/financial_v2_controller.dart
Normal file
61
siro_admin/lib/controller/admin/financial_v2_controller.dart
Normal 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');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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'],
|
||||
);
|
||||
}
|
||||
}
|
||||
60
siro_admin/lib/controller/admin/kazan_controller.dart
Normal file
60
siro_admin/lib/controller/admin/kazan_controller.dart
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
172
siro_admin/lib/controller/admin/passenger_admin_controller.dart
Normal file
172
siro_admin/lib/controller/admin/passenger_admin_controller.dart
Normal 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();
|
||||
}
|
||||
}
|
||||
83
siro_admin/lib/controller/admin/promo_controller.dart
Normal file
83
siro_admin/lib/controller/admin/promo_controller.dart
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
102
siro_admin/lib/controller/admin/quality_controller.dart
Normal file
102
siro_admin/lib/controller/admin/quality_controller.dart
Normal 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
|
||||
}
|
||||
}
|
||||
590
siro_admin/lib/controller/admin/register_captain_controller.dart
Normal file
590
siro_admin/lib/controller/admin/register_captain_controller.dart
Normal 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 Driver’s License'.tr,
|
||||
content: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const Icon(Icons.warning, size: 48, color: Colors.red),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
'Your driver’s 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 driver’s 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 Driver’s License'.tr,
|
||||
content: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const Icon(Icons.warning, size: 48, color: Colors.red),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
'Your driver’s 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 driver’s 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 driver’s 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 driver’s 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 driver’s 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 driver’s 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 {}
|
||||
}
|
||||
}
|
||||
73
siro_admin/lib/controller/admin/ride_admin_controller.dart
Normal file
73
siro_admin/lib/controller/admin/ride_admin_controller.dart
Normal 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;
|
||||
// }
|
||||
62
siro_admin/lib/controller/admin/security_v2_controller.dart
Normal file
62
siro_admin/lib/controller/admin/security_v2_controller.dart
Normal 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();
|
||||
}
|
||||
}
|
||||
81
siro_admin/lib/controller/admin/staff_controller.dart
Normal file
81
siro_admin/lib/controller/admin/staff_controller.dart
Normal 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();
|
||||
}
|
||||
}
|
||||
512
siro_admin/lib/controller/admin/static_controller.dart
Normal file
512
siro_admin/lib/controller/admin/static_controller.dart
Normal 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);
|
||||
}
|
||||
195
siro_admin/lib/controller/admin/wallet_admin_controller.dart
Normal file
195
siro_admin/lib/controller/admin/wallet_admin_controller.dart
Normal 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();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user