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,27 @@
import 'package:secure_string_operations/secure_string_operations.dart';
import '../env/env.dart';
import 'char_map.dart';
class AK {
static final String basicAuthCredentials =
X.r(X.r(X.r(Env.basicAuthCredentials, cn), cC), cs);
static final String visionApi = X.r(X.r(X.r(Env.visionApi, cn), cC), cs);
static final String smsPasswordEgypt =
X.r(X.r(X.r(Env.smsPasswordEgypt, cn), cC), cs);
static final String ocpApimSubscriptionKey = Env.ocpApimSubscriptionKey;
static final String anthropicAIkeySeferNew =
X.r(X.r(X.r(Env.anthropicAIkeySeferNew, cn), cC), cs);
static final String anthropicAIkeySeferNewHamzaayedpython =
X.r(X.r(X.r(Env.anthropicAIkeySeferNewHamzaayedpython, cn), cC), cs);
static final String emailService = (Env.emailService);
static final String allowed = Env.allowed;
static final String passnpassenger = X
.r(X.r(X.r(Env.passnpassenger, cn), cC), cs)
.toString()
.split(Env.addd)[0];
static final String newId = Env.newId;
///////////
static final String keyOfApp = X.r(X.r(X.r(Env.keyOfApp, cn), cC), cs);
}

View File

@@ -0,0 +1,98 @@
class BoxName {
static const String driverID = "driverID";
static const String countryCode = "countryCode";
static const String googlaMapApp = "googlaMapApp";
static const String keyOfApp = 'keyOfApp';
static const String initializationVector = 'initializationVector';
static const String firstTimeLoadKey = 'firstTimeLoadKey';
static const String jwt = "jwt";
static const String fingerPrint = "fingerPrint";
static const String deviceFingerprint = "deviceFingerprint";
static const String hmac = "hmac";
static const String payMobApikey = "payMobApikey";
static const String employeename = "employeename";
static const String refreshToken = "refreshToken";
static const String tokenParent = "tokenParent";
static const String lang = "lang";
static const String gender = "gender";
static const String serverChosen = "serverChosen";
static const String carType = "carType";
static const String carPlate = "carPlate";
static const String isVerified = '0';
static const String statusDriverLocation = "statusDriverLocation";
static const String password = "password";
static const String arrivalTime = "arrivalTime";
static const String passwordDriver = "passwordDriver";
static const String agreeTerms = "agreeTerms";
static const String addWork = 'addWork';
static const String addHome = 'addHome';
static const String tipPercentage = 'tipPercentage';
static const String accountIdStripeConnect = "accountIdStripeConnect";
static const String faceDetectTimes = "faceDetectTimes";
static const String sosPhonePassenger = "sosPhonePassenger";
static const String sosPhoneDriver = "sosPhoneDriver";
static const String passengerID = "pasengerID";
static const String phone = "phone";
static const String phoneDriver = "phoneDriver";
static const String dobDriver = "dobDriver";
static const String sexDriver = "sexDriver";
static const String lastNameDriver = "lastNameDriver";
static const String name = "name";
static const String locationPermission = "locationPermission";
static const String nameDriver = "nameDriver";
static const String driverPhotoUrl = "driverPhotoUrl";
static const String passengerPhotoUrl = "passengerPhotoUrl";
static const String email = "email";
static const String emailDriver = "emailDriver";
static const String tokens = "tokens";
static const String tokenFCM = "tokenFCM";
static const String tokenDriver = "tokenDriver";
static const String cardNumber = "cardNumber";
static const String cardNumberDriver = "cardNumberDriver";
static const String cardHolderName = "cardHolderName";
static const String cardHolderNameDriver = "cardHolderNameDriver";
static const String expiryDate = "expiryDate";
static const String expiryDateDriver = "expiryDateDriver";
static const String cvvCode = "cvvCode";
static const String cvvCodeDriver = "cvvCodeDriver";
static const String passengerWalletDetails = "passengerWalletDetails";
static const String passengerWalletTotal = "passengerWalletTotal";
static const String passengerWalletFound = "passengerWalletFound";
static const String periods = 'periods';
static const String onBoarding = 'onBoarding';
static const String stripePublishableKey = 'stripe_publishableKe';
static const String apiKeyRun = 'apiKeyRun';
static const String serverAPI = 'serverAPI';
static const String secretKey = 'secretKey';
static const String basicAuthCredentials = 'basicAuthCredentials';
static const String mapAPIKEY = 'mapAPIKEY';
static const String twilloRecoveryCode = 'twilloRecoveryCode';
static const String accountSIDTwillo = 'accountSIDTwillo';
static const String authTokenTwillo = 'authTokenTwillo';
static const String chatGPTkey = 'chatGPTkey';
static const String chatGPTkeySefer = 'chatGPTkeySefer';
static const String transactionCloude = 'transactionCloude';
static const String visionApi = 'visionApi';
static const String vin = "vin";
static const String isvibrate = "isvibrate";
static const String make = "make";
static const String model = "model";
static const String year = "year";
static const String expirationDate = "expirationDate";
static const String color = "color";
static const String owner = "owner";
static const String registrationDate = "registrationDate";
static const String recentLocations = 'recentLocations';
static const String tripData = 'tripData';
static const String parentTripSelected = 'parentTripSelected';
}
class Service {
static const String driverPass = 'MG6DEJZSczBT6Rx0jOlehQ==';
static const String payMobApikey = 'payMobApikey';
static const String initializationVector = 'initializationVector';
static const String keyOfApp = 'keyOfApp';
static const String FCM_PRIVATE_KEY = 'FCM_PRIVATE_KEY';
}

View File

@@ -0,0 +1,68 @@
Map<String, String> cn = {
"0": "3",
"1": "7",
"2": "1",
"3": "9",
"4": "0",
"5": "5",
"6": "2",
"7": "6",
"8": "4",
"9": "8"
};
Map<String, String> cs = {
"a": "q",
"b": "x",
"c": "f",
"d": "y",
"e": "j",
"f": "u",
"g": "k",
"h": "w",
"i": "o",
"j": "e",
"k": "g",
"l": "r",
"m": "n",
"n": "b",
"o": "i",
"p": "v",
"q": "a",
"r": "l",
"s": "z",
"t": "c",
"u": "h",
"v": "p",
"w": "t",
"x": "d",
"y": "s",
"z": "m"
};
Map<String, String> cC = {
"A": "Q",
"B": "X",
"C": "F",
"D": "Y",
"E": "J",
"F": "U",
"G": "K",
"H": "W",
"I": "O",
"J": "E",
"K": "G",
"L": "R",
"M": "N",
"N": "B",
"O": "I",
"P": "V",
"Q": "A",
"R": "L",
"S": "Z",
"T": "C",
"U": "H",
"V": "P",
"W": "T",
"X": "D",
"Y": "S",
"Z": "M"
};

View File

@@ -0,0 +1,25 @@
import 'package:flutter/material.dart';
class AppColor {
static const Color primaryColor = Color(0xFF2563EB); // Modern Blue
static const Color primaryLight = Color(0xFFDBEAFE);
static const Color writeColor = Color(0xFF1E293B); // Darker Slate
static const Color surfaceColor = Color(0xFFF8FAFC);
static const Color bronze = Color(0xFFCD7F32);
static const Color goldenBronze = Color(0xFFB87333);
static const Color gold = Color(0xFFD4AF37);
static const Color secondaryColor = Colors.white;
static const Color accentColor = Color(0xFF64748B); // Slate Grey
static const Color greyColor = Color(0xFF94A3B8);
static const Color redColor = Color(0xFFEF4444);
static const Color greenColor = Color(0xFF10B981);
static const Color blueColor = Color(0xFF3B82F6);
static const Color yellowColor = Color(0xFFF59E0B);
static Color deepPurpleAccent = const Color(0xFF7C3AED);
static Color glassEffect = Colors.white.withOpacity(0.1);
}

View File

@@ -0,0 +1,141 @@
import 'dart:convert';
import 'package:crypto/crypto.dart';
import '../main.dart';
import 'box_name.dart';
class AC {
gAK() async {
if (box.read(BoxName.apiKeyRun).toString() != 'run') {
// var res = await CRUD().get(link: AppLink.getApiKey, payload: {});
// var decod = jsonDecode(res);
// print(decod);
// Map<String, dynamic> jsonData = {};
// for (var i = 0; i < decod['message'].length; i++) {
// String h = decod['message'][i]['hashed_key'].toString();
// String retrievedString = r(r(r(h, cn), cC), cs);
// await storage.write(
// key: decod['message'][i]['name'].toString(),
// value: retrievedString.toString(),
// );
// //
// String name = decod['message'][i]['name'].toString();
// String value = decod['message'][i]['hashed_key'].toString();
// jsonData[name] = value;
// }
// String jsonString = json.encode(jsonData);
// print(jsonString);
// box.write(BoxName.apiKeyRun, 'run');
}
}
String q(String b, String c) {
final d = utf8.encode(c);
final e = utf8.encode(b);
final f = Hmac(sha256, d);
final g = f.convert(e);
final h = g.bytes;
final i = base64Url.encode(h);
return i;
}
String j(String k, String l) {
final m = utf8.encode(l);
final n = base64Url.decode(k);
final o = Hmac(sha256, m);
final p = o.convert(n);
final q = utf8.decode(p.bytes);
return q;
}
String a(String b, String c) {
int d = b.length;
int e = d ~/ 4;
List<String> f = [];
for (int g = 0; g < d; g += e) {
int h = g + e;
if (h > d) {
h = d;
}
String i = b.substring(g, h);
f.add(i);
}
print(f);
Map<String, String> j = {};
j['birinci'] = f[4];
j['ikinci'] = f[2];
j['üçüncü'] = c + f[1];
j['dördüncü'] = f[0];
j['beş'] = f[3];
String k = '';
j.forEach((l, m) {
k += m;
});
return k;
}
Map<String, String> n(String o, String c) {
String p = o.replaceAll(c, '');
Map<String, String> q = {};
q['birinci'] = p[p.length - 5] + p[p.length - 3];
q['ikinci'] = p[p.length - 1] + p[p.length - 15];
q['üçüncü'] = p[p.length - 9] + p[p.length - 12];
q['dördüncü'] = p[p.length - 11] + p[p.length - 6];
q['beş'] = p[p.length - 2] + p[p.length - 8];
return q;
}
String c(String a, Map<String, String> b) {
StringBuffer c = StringBuffer();
c.write(a);
String d = "Bl";
c.write(b[d] ?? d);
StringBuffer e = StringBuffer();
String f = c.toString();
for (int g = 0; g < f.length; g++) {
String h = f[g];
e.write(b[h] ?? h);
}
return e.toString();
}
String r(String a, Map<String, String> b) {
StringBuffer c = StringBuffer();
String d = "Bl";
int e = d.length;
for (int f = 0; f < a.length; f++) {
String g = a[f];
String h = b.keys.firstWhere(
(i) => b[i] == g,
orElse: () => g,
);
c.write(h);
}
String j = c.toString();
if (j.endsWith(d)) {
j = j.substring(0, j.length - e);
}
return j;
}
}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,105 @@
import '../env/env.dart';
import '../main.dart';
import 'box_name.dart';
class AppLink {
// static final String seferPaymentServer = Env.seferPaymentServer;
// // static final String seferPaymentServer = '${Env.seferCairoServer}/ride';
static final String seferAlexandriaServer = Env.seferAlexandriaServer;
static final String seferCairoServer = Env.seferCairoServer;
static final String seferGizaServer = Env.seferGizaServer;
static String paymentServer = 'https://walletintaleq.intaleq.xyz/v1/main';
static String locationServer = 'https://location.intaleq.xyz/intaleq/ride/location';
static String location = locationServer;
static String locationServerSide = 'https://location.intaleq.xyz/intaleq/ride/location';
static String mapSaasRoute = 'https://map-saas.intaleqapp.com/api/maps/route';
static String mapSaasPlaces = 'https://map-saas.intaleqapp.com/api/geocoding/places';
static const String routeApiBaseUrl = "https://routesjo.intaleq.xyz/route/v1/driving";
static final String syria = 'https://syria.intaleq.xyz/intaleq';
static String seferPaymentServer0 = box.read('seferPaymentServer');
static final String endPoint = 'https://api.intaleq.xyz/intaleq_v3';
static final String rideServer = 'https://rides.intaleq.xyz/intaleq';
// static final String server = Env.serverPHP;
static String getBestDriver = "$server/Admin/driver/getBestDriver.php";
static final String server = 'https://api.intaleq.xyz/intaleq_v3';
static final String jwtService = '$server/jwtService.php';
static String addError = "$server/Admin/errorApp.php";
// static final String endPoint = box.read(BoxName.serverChosen);
// static final String server = Env.seferCairoServer;
// static const String server = "https://sefer.click/sefer/sefer";
static String googleMapsLink = 'https://maps.googleapis.com/maps/api/';
static String llama = 'https://api.llama-api.com/chat/completions';
static String getTokens = "$server/ride/firebase/get.php";
static String gemini =
'https://generativelanguage.googleapis.com/v1beta3/models/text-bison-001:generateText';
static String uploadEgypt = "$server/uploadEgypt.php";
static String auth = '$server/auth';
static String login = "$serviceApp/login.php";
static String register = "$serviceApp/register.php";
static String signUp = "$auth/signup.php";
static String sendVerifyEmail = "$auth/sendVerifyEmail.php";
static String passengerRemovedAccountEmail =
"$auth/passengerRemovedAccountEmail.php";
static String verifyEmail = "$auth/verifyEmail.php";
static String getPromptDriverDocumentsEgypt =
"$server/auth/captin/getPromptDriverDocumentsEgypt.php";
//===================Auth Captin============
static String authCaptin = '$server/auth/captin';
static String loginCaptin = "$authCaptin/login.php";
static String signUpCaptin = "$authCaptin/register.php";
static String sendVerifyEmailCaptin = "$authCaptin/sendVerifyEmail.php";
static String verifyEmailCaptin = "$authCaptin/verifyEmail.php";
static String removeUser = "$authCaptin/removeAccount.php";
static String deletecaptainAccounr = "$authCaptin/deletecaptainAccounr.php";
static String updateAccountBank = "$authCaptin/updateAccountBank.php";
static String getAccount = "$authCaptin/getAccount.php";
static String test = "$server/test.php";
static String serviceApp = "$server/serviceapp";
static String getPassengersByPhone = "$serviceApp/getPassengersByPhone.php";
static String getDriverByPhone = "$serviceApp/getDriverByPhone.php";
static String getDriverByNational = "$serviceApp/getDriverByNational.php";
static String updateDriver = "$serviceApp/updateDriver.php";
static String updateDriverToActive = "$serviceApp/updateDriverToActive.php";
static String getNewDriverRegister = "$serviceApp/getNewDriverRegister.php";
static String getDriversPhoneNotComplete =
"$serviceApp/getDriversPhoneNotComplete.php";
static String addWelcomeDriverNote = "$serviceApp/addWelcomeDriverNote.php";
static String getDriverNotCompleteRegistration =
"$serviceApp/getDriverNotCompleteRegistration.php";
static String deleteDriverNotCompleteRegistration =
"$serviceApp/deleteDriverNotCompleteRegistration.php";
static String getDriversWaitingActive =
"$serviceApp/getDriversWaitingActive.php";
static String getPassengersNotCompleteRegistration =
"$serviceApp/getPassengersNotCompleteRegistration.php";
static String addNotesDriver = "$serviceApp/addNotesDriver.php";
static String getDriverDetailsForActivate =
"$serviceApp/getDriverDetailsForActivate.php";
static String getCarPlateNotEdit = "$serviceApp/getCarPlateNotEdit.php";
static String getdriverWithoutCar = "$serviceApp/getdriverWithoutCar.php";
static String getBestDriverGiza =
"https://gizasefer.online/sefer/Admin/driver/getBestDriver.php";
static String getBestDriverAlexandria =
"$seferAlexandriaServer/Admin/driver/getBestDriver.php";
static String addNotesPassenger = "$serviceApp/addNotesPassenger.php";
static String editCarPlate = "$serviceApp/editCarPlate.php";
static String addDriverWantWork = "$serviceApp/work/addDriverWantWork.php";
static String addCarWantWork = "$serviceApp/work/addCarWantWork.php";
static String getComplaintAllData = "$serviceApp/getComplaintAllData.php";
static String getComplaintAllDataForDriver =
"$serviceApp/getComplaintAllDataForDriver.php";
static String addCriminalDocuments = "$authCaptin/addCriminalDocuments.php";
static String ride = '$server/ride';
static String addRegisrationCar = "$ride/RegisrationCar/add.php";
static String addCartoDriver = "$serviceApp/addCartoDriver.php";
static String getRegisrationCar = "$ride/RegisrationCar/get.php";
static String updateRegisrationCar = "$ride/RegisrationCar/update.php";
}

View File

@@ -0,0 +1,65 @@
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
import '../main.dart';
import 'box_name.dart';
import 'colors.dart';
class AppStyle {
static TextStyle headTitle = TextStyle(
fontWeight: FontWeight.bold,
fontSize: 36,
color: AppColor.accentColor,
fontFamily: box.read(BoxName.lang) == 'ar'
// ?GoogleFonts.markaziText().fontFamily
? GoogleFonts.markaziText().fontFamily
: GoogleFonts.inter().fontFamily);
static TextStyle headTitle2 = TextStyle(
fontWeight: FontWeight.bold,
fontSize: 24,
color: AppColor.writeColor,
fontFamily: box.read(BoxName.lang) == 'ar'
? GoogleFonts.markaziText().fontFamily
: GoogleFonts.inter().fontFamily);
static TextStyle title = TextStyle(
fontWeight: FontWeight.normal,
fontSize: 16,
color: AppColor.writeColor,
fontFamily: box.read(BoxName.lang) == 'ar'
? GoogleFonts.markaziText().fontFamily
: GoogleFonts.inter().fontFamily);
static TextStyle subtitle = TextStyle(
fontWeight: FontWeight.bold,
fontSize: 12,
color: AppColor.writeColor,
fontFamily: box.read(BoxName.lang) == 'ar'
? GoogleFonts.markaziText().fontFamily
: GoogleFonts.inter().fontFamily);
static TextStyle number = const TextStyle(
fontWeight: FontWeight.bold,
fontSize: 14,
color: AppColor.writeColor,
fontFamily: 'digit');
static BoxDecoration boxDecoration = BoxDecoration(
boxShadow: [
BoxShadow(
color: AppColor.accentColor.withValues(alpha: 0.1),
blurRadius: 10,
offset: const Offset(0, 4)),
],
color: AppColor.secondaryColor,
borderRadius: BorderRadius.circular(16));
static BoxDecoration boxDecoration1 = BoxDecoration(
boxShadow: [
BoxShadow(
color: Colors.black.withValues(alpha: 0.05),
blurRadius: 15,
offset: const Offset(0, 8)),
],
color: AppColor.secondaryColor,
borderRadius: BorderRadius.circular(20),
);
}

View File

@@ -0,0 +1,9 @@
class TableName {
static const String placesFavorite = "placesFavorite";
static const String recentLocations = "recentLocations";
static const String carLocations = "carLocations";
static const String driverOrdersRefuse = "driverOrdersRefuse";
static const String rideLocation = "rideLocation";
static const String faceDetectTimes = "faceDetectTimes";
static const String captainNotification = "captainNotification";
}

View File

@@ -0,0 +1,78 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:siro_service/constant/links.dart';
import 'package:siro_service/controller/functions/crud.dart';
import 'package:siro_service/controller/functions/device_helper.dart';
import 'package:siro_service/print.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
class RegisterController extends GetxController {
final firstName = TextEditingController();
final lastName = TextEditingController();
final email = TextEditingController();
final phone = TextEditingController();
final password = TextEditingController();
final FlutterSecureStorage storage = const FlutterSecureStorage();
final formKey = GlobalKey<FormState>();
var isLoading = false.obs;
void register() async {
if (formKey.currentState!.validate()) {
isLoading.value = true;
// الحصول على البصمة
String fingerprint = await DeviceHelper.getDeviceFingerprint();
var payload = {
"first_name": firstName.text,
"last_name": lastName.text,
"email": email.text,
"phone": phone.text,
"password": password.text,
"fingerprint": fingerprint,
};
Log.print('🚀 Register Payload: $payload');
var res = await CRUD().post(link: AppLink.register, payload: payload);
Log.print('📥 Register Response: $res');
isLoading.value = false;
if (res != 'failure' && res is Map && res['status'] == 'success') {
// حفظ كلمة المرور للدخول التلقائي لاحقاً
await storage.write(key: 'password', value: password.text);
Get.defaultDialog(
title: "نجاح",
middleText: res['message']['message'] ?? "تم تقديم طلبك بنجاح. يرجى انتظار موافقة الإدارة.",
onConfirm: () {
Get.back(); // close dialog
Get.back(); // return to login
},
textConfirm: "موافق",
);
} else {
Get.snackbar(
"خطأ",
res is Map ? res['message'].toString() : "فشل تقديم طلب التسجيل",
backgroundColor: Colors.red.withOpacity(0.7),
colorText: Colors.white,
);
}
}
}
@override
void onClose() {
firstName.dispose();
lastName.dispose();
email.dispose();
phone.dispose();
password.dispose();
super.onClose();
}
}

View File

@@ -0,0 +1,71 @@
import 'dart:convert';
import 'package:get/get.dart';
import '../../constant/colors.dart';
import '../../constant/links.dart';
import '../print.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 = res['message'];
// Log.print('driver: ${driver}');
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 = 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 = res['message'];
update();
} else {
Get.snackbar('error', '', backgroundColor: AppColor.redColor);
}
}
@override
void onInit() {
getBestDriver();
super.onInit();
}
}

View File

@@ -0,0 +1,110 @@
import 'dart:io';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:get/get.dart';
import 'package:siro_service/controller/local_notification.dart';
import '../../constant/box_name.dart';
import '../../main.dart';
import '../../print.dart';
@pragma('vm:entry-point')
Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
// If you're going to use other Firebase services in the background, such as Firestore,
// make sure you call `initializeApp` before using other Firebase services.
Log.print("Handling a background message: ${message.messageId}");
if (message.data.isNotEmpty && message.notification != null) {
// في وضع الخلفية، يفضل إرسال إشعار محلي أو تحديث البيانات الصامتة
}
}
class FirebaseMessagesController extends GetxController {
final fcmToken = FirebaseMessaging.instance;
List<String> tokens = [];
List dataTokens = [];
late String driverID;
late String driverToken;
NotificationSettings? notificationSettings;
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);
}
}
// NotificationController notificationController =
// Get.isRegistered<NotificationController>()
// ? Get.find<NotificationController>()
// : Get.put(NotificationController());
Future getToken() async {
fcmToken.getToken().then((token) {
// Log.print('fcmToken: ${token}');
box.write(BoxName.tokenFCM, (token.toString()));
});
// 🔹 الاشتراك في topic
await fcmToken.subscribeToTopic("service"); // أو "users" حسب نوع المستخدم
Log.print("Subscribed to 'service' topic ✅");
FirebaseMessaging.onMessage.listen((RemoteMessage message) {
// If the app is in the background or terminated, show a system tray message
if (message.data.isNotEmpty && message.notification != null) {
fireBaseTitles(message);
}
});
// استخدام الدالة العامة للهاندلر في الخلفية
FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) {
if (message.data.isNotEmpty && message.notification != null) {
fireBaseTitles(message);
}
});
}
Future<void> fireBaseTitles(RemoteMessage message) async {
// [!! تعديل !!]
// اقرأ "النوع" من حمولة البيانات، وليس من العنوان
String category = message.data['category'] ?? '';
// اقرأ العنوان (للعرض)
String title = message.notification?.title ?? '';
String body = message.notification?.body ?? '';
if (category == 'new_service_request') {
// <-- مثال: كان 'Order'.tr
Log.print('message: ${message}');
if (Platform.isAndroid) {
NotificationController().showNotification(title, body, 'Order');
}
}
}
}

View File

@@ -0,0 +1,109 @@
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 '../../constant/api_key.dart';
import '../../constant/box_name.dart';
import '../../constant/colors.dart';
import '../../constant/links.dart';
import '../../constant/style.dart';
import '../../main.dart';
import 'functions/crud.dart';
class FirebaseMessagesController extends GetxController {
final fcmToken = FirebaseMessaging.instance;
List<String> tokens = [];
List dataTokens = [];
late String driverID;
late String driverToken;
NotificationSettings? notificationSettings;
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 CRUD().post(link: AppLink.getTokens, payload: {});
if (res != 'failure' && res['status'] == 'success') {
dataTokens = res['data'];
for (var i = 0; i < dataTokens.length; i++) {
tokens.add(res['data'][i]['token']);
}
box.write(BoxName.tokens, tokens);
} else {
Get.snackbar("Warning".tr, "Server Error".tr,
backgroundColor: AppColor.redColor);
}
}
Future getToken() async {
fcmToken.getToken().then((token) {
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);
}
});
FirebaseMessaging.onBackgroundMessage((RemoteMessage message) async {
// Handle background message
if (message.data.isNotEmpty && message.notification != null) {
fireBaseTitles(message);
}
});
FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) {
if (message.data.isNotEmpty && message.notification != null) {
fireBaseTitles(message);
}
});
}
void fireBaseTitles(RemoteMessage message) {
if (message.notification!.title! == 'Order'.tr) {
} else if (message.notification!.title! == 'Apply Ride'.tr) {
var passengerList = message.data['passengerList'];
var myList = jsonDecode(passengerList) as List<dynamic>;
driverID = myList[0].toString();
}
}
}

View File

@@ -0,0 +1,386 @@
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:crypto/crypto.dart';
import 'package:get/get.dart';
import 'package:http/http.dart' as http;
import 'package:siro_service/constant/box_name.dart';
import 'package:siro_service/constant/links.dart';
import 'package:siro_service/controller/functions/encrypt_decrypt.dart';
import 'package:siro_service/env/env.dart';
import 'package:siro_service/controller/functions/security_helper.dart';
import 'package:siro_service/main.dart';
import 'package:siro_service/print.dart';
import '../../constant/api_key.dart';
class CRUD {
static bool _isRefreshingJWT = false;
static String? _appSignature;
static String _lastErrorSignature = '';
static DateTime _lastErrorTimestamp = DateTime(2000);
static const Duration _errorLogDebounceDuration = Duration(minutes: 1);
// ── JWT Validity Check (No external libs) ──────────────────────
static bool _isJwtValid(String? token) {
if (token == null || token.isEmpty) return false;
try {
final parts = token.split('.');
if (parts.length != 3) return false;
String payload = parts[1];
switch (payload.length % 4) {
case 2:
payload += '==';
break;
case 3:
payload += '=';
break;
}
final decoded = jsonDecode(utf8.decode(base64Url.decode(payload)));
final exp = decoded['exp'];
if (exp == null) return false;
// 30 seconds buffer
return DateTime.now().millisecondsSinceEpoch < (exp * 1000 - 30000);
} catch (_) {
return false;
}
}
static Future<void> addError(
String error, String details, String where) async {
try {
final currentErrorSignature = '$where-$error';
final now = DateTime.now();
if (currentErrorSignature == _lastErrorSignature &&
now.difference(_lastErrorTimestamp) < _errorLogDebounceDuration) {
return;
}
_lastErrorSignature = currentErrorSignature;
_lastErrorTimestamp = now;
final userId =
box.read(BoxName.driverID) ?? box.read(BoxName.passengerID);
final userType = 'Service';
final phone = box.read(BoxName.phone) ?? box.read(BoxName.phoneDriver);
CRUD().post(
link: AppLink.addError,
payload: {
'error': error.toString(),
'userId': userId.toString(),
'userType': userType,
'phone': phone.toString(),
'device': where,
'details': details,
},
);
} catch (e) {}
}
String _getFpHeader() {
return box.read(BoxName.fingerPrint)?.toString() ?? '';
}
String _generateHmac(String body, String timestamp, String nonce) {
// نستخدم المفتاح الخاص بالمستخدم (المخزن في البوكس) كـ HMAC Secret
final hmacSecret = box.read(BoxName.hmac) ?? '';
final payload = body + timestamp + nonce;
final key = utf8.encode(hmacSecret);
final bytes = utf8.encode(payload);
final hmacSha256 = Hmac(sha256, key);
final result = hmacSha256.convert(bytes).toString();
Log.print('🔐 [HMAC-DEBUG] Secret: $hmacSecret');
Log.print('🔐 [HMAC-DEBUG] Body(${body.length}): "$body"');
Log.print('🔐 [HMAC-DEBUG] TS: $timestamp | Nonce: $nonce');
Log.print('🔐 [HMAC-DEBUG] Result: $result');
return result;
}
// ═══════════════════════════════════════════════════════════════
// _makeRequest — Central Request Handler
// ───────────────────────────────────────────────────────────────
Future<dynamic> _makeRequest({
required String link,
Map<String, dynamic>? payload,
required Map<String, String> headers,
}) async {
const totalTimeout = Duration(seconds: 60);
// توليد بيانات الـ HMAC للطلب الحالي
final timestamp = DateTime.now().millisecondsSinceEpoch.toString();
final nonce =
DateTime.now().microsecondsSinceEpoch.toString(); // Nonce فريد
// تحويل الـ payload إلى string لمحاكة ما سيصل للسيرفر (php://input)
String bodyString = '';
if (payload != null && payload.isNotEmpty) {
// الـ http.post يرسل البيانات كـ x-www-form-urlencoded
bodyString = payload.keys
.map((key) =>
"$key=${Uri.encodeQueryComponent(payload[key].toString())}")
.join("&");
}
final hmacSignature = _generateHmac(bodyString, timestamp, nonce);
// إضافة هيدرات الـ HMAC
headers['X-HMAC-Auth'] = hmacSignature;
headers['X-Timestamp'] = timestamp;
headers['X-Nonce'] = nonce;
Future<http.Response> doPost() {
final url = Uri.parse(link);
return http
.post(url, body: payload, headers: headers)
.timeout(totalTimeout);
}
http.Response? response;
int attempts = 0;
final requestId =
DateTime.now().millisecondsSinceEpoch.toString().substring(7);
Log.print('🚀 [REQ-$requestId] $link');
Log.print('🔑 [FP-$requestId] ${headers['X-Device-FP']}');
Log.print('🔏 [SIGN-$requestId] ${headers['X-App-Signature']}');
if (payload != null) Log.print('📦 [PAYLOAD-$requestId] $payload');
while (attempts < 3) {
try {
attempts++;
response = await doPost();
break;
} on SocketException catch (_) {
Log.print('⚠️ SocketException attempt $attempts$link');
if (attempts >= 3) return 'no_internet';
await Future.delayed(const Duration(seconds: 1));
} on TimeoutException catch (_) {
Log.print('⚠️ TimeoutException attempt $attempts$link');
if (attempts >= 3) return 'failure';
} catch (e) {
if (e.toString().contains('errno = 9') && attempts < 3) {
await Future.delayed(const Duration(milliseconds: 500));
continue;
}
addError(
'HTTP Exception: $e', 'Try: $attempts', 'CRUD._makeRequest $link');
return 'failure';
}
}
if (response == null) return 'failure';
final sc = response.statusCode;
final body = response.body;
Log.print('📥 [RES-$requestId] [$sc] $link');
Log.print('📄 [BODY-$requestId] $body');
if (sc >= 200 && sc < 300) {
try {
return jsonDecode(body);
} catch (e, st) {
addError(
'JSON Decode Error', 'Body: $body\n$st', 'CRUD._makeRequest $link');
return 'failure';
}
}
if (sc == 401) {
if (!_isRefreshingJWT && !link.contains('errorApp.php')) {
_isRefreshingJWT = true;
try {
await getJWT();
} finally {
_isRefreshingJWT = false;
}
}
return 'token_expired';
}
if (sc >= 500) {
addError('Server 5xx', 'SC: $sc\nBody: $body', 'CRUD._makeRequest $link');
return 'failure';
}
return 'failure';
}
// ═══════════════════════════════════════════════════════════════
// post — standard POST
// ═══════════════════════════════════════════════════════════════
Future<dynamic> post({
required String link,
Map<String, dynamic>? payload,
}) async {
String token = r(box.read(BoxName.jwt) ?? '').toString().split(Env.addd)[0];
if (!_isJwtValid(token) &&
!_isRefreshingJWT &&
!link.contains('login.php')) {
_isRefreshingJWT = true;
try {
await getJWT();
token = r(box.read(BoxName.jwt) ?? '').toString().split(Env.addd)[0];
} finally {
_isRefreshingJWT = false;
}
}
// Initialize app signature if null
_appSignature ??= await SecurityHelper.getAppSignature();
final headers = {
'Content-Type': 'application/x-www-form-urlencoded',
'Authorization': 'Bearer $token',
'X-Device-FP': _getFpHeader(),
'X-App-Signature': _appSignature ?? '',
};
return await _makeRequest(link: link, payload: payload, headers: headers);
}
// ═══════════════════════════════════════════════════════════════
// get — standard GET (uses POST method in this architecture)
// ═══════════════════════════════════════════════════════════════
Future<dynamic> get({
required String link,
Map<String, dynamic>? payload,
}) async {
return await post(link: link, payload: payload);
}
// ═══════════════════════════════════════════════════════════════
// getJWT — V1 Login Flow
// ═══════════════════════════════════════════════════════════════
Future<void> getJWT() async {
var payload = {
'fingerprint': _getFpHeader(),
'password': box.read(BoxName.password) ?? '',
'aud': 'service',
};
// Initialize app signature if null
_appSignature ??= await SecurityHelper.getAppSignature();
final headers = {
'Content-Type': 'application/x-www-form-urlencoded',
'X-App-Signature': _appSignature ?? '',
};
final response = await _makeRequest(
link: AppLink.login, payload: payload, headers: headers);
if (response != 'failure' &&
response is Map &&
response['status'] == 'success') {
final jwt = response['message']['jwt'];
final hmac = response['message']['hmac'];
Log.print('jwt: $jwt');
Log.print('hmac_key: $hmac');
await box.write(BoxName.jwt, c(jwt));
if (hmac != null) {
await box.write(BoxName.hmac, hmac);
final verify = box.read(BoxName.hmac);
Log.print('✅ Verified stored HMAC: $verify');
}
}
}
// ─────────────────────────────────────────────────────────────
// Service Specific Methods (Preserved)
// ─────────────────────────────────────────────────────────────
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();
}
return 'failure';
}
Future<dynamic> getAgoraToken({
required String channelName,
required String uid,
}) async {
var res = await http.get(
Uri.parse(
'https://orca-app-b2i85.ondigitalocean.app/token?channelName=$channelName'),
headers: {'Authorization': 'Bearer '});
if (res.statusCode == 200) {
var response = jsonDecode(res.body);
return response['token'];
}
return 'failure';
}
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'
};
var data = json.encode({
"model": "Llama-3-70b-Inst-FW",
"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;
}
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) {}
}
}

View File

@@ -0,0 +1,49 @@
import 'dart:io';
import 'package:device_info_plus/device_info_plus.dart';
import 'package:flutter/foundation.dart';
import 'package:siro_service/constant/box_name.dart';
import 'package:siro_service/controller/functions/encrypt_decrypt.dart';
import '../../main.dart';
import '../../print.dart';
class DeviceHelper {
static Future<String> getDeviceFingerprint() async {
await EncryptionHelper.initialize();
final DeviceInfoPlugin deviceInfoPlugin = DeviceInfoPlugin();
var deviceData;
try {
if (Platform.isAndroid) {
AndroidDeviceInfo androidInfo = await deviceInfoPlugin.androidInfo;
deviceData = androidInfo.toMap();
} else if (Platform.isIOS) {
IosDeviceInfo iosInfo = await deviceInfoPlugin.iosInfo;
deviceData = iosInfo.toMap();
} else {
throw UnsupportedError('Unsupported platform');
}
final String deviceId = Platform.isAndroid
? deviceData['id'] ??
deviceData['androidId'] ??
deviceData['fingerprint'] ??
'unknown'
: deviceData['identifierForVendor'] ?? 'unknown';
final String deviceModel = deviceData['model'] ?? 'unknown';
Log.print('DeviceId: $deviceId');
Log.print('DeviceModel: $deviceModel');
final String fingerprint =
EncryptionHelper.instance.encryptData('${deviceId}_$deviceModel');
Log.print('Generated Fingerprint: $fingerprint');
box.write(BoxName.fingerPrint, fingerprint);
return (fingerprint);
} catch (e) {
Log.print('Error generating device fingerprint: $e');
return '';
}
}
}

View File

@@ -0,0 +1,87 @@
import 'package:encrypt/encrypt.dart' as encrypt;
import 'package:flutter/foundation.dart';
import 'package:siro_service/env/env.dart';
import 'package:secure_string_operations/secure_string_operations.dart';
import 'package:siro_service/constant/char_map.dart';
import 'package:siro_service/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...");
// Read stored keys
String keyOfApp = r(Env.keyOfApp).toString().split(Env.addd)[0];
String initializationVector = r(Env.initializationVector).toString().split(Env.addd)[0];
Log.print('Key Length: ${keyOfApp.length}');
Log.print('IV Length: ${initializationVector.length}');
// 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) {
Log.print('Encrypting: $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) {
Log.print('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) {
var res = X.r(X.r(X.r(string, cn), cC), cs).toString();
// Log.print('r($string) => $res');
return res;
}
c(String string) {
return X.c(X.c(X.c(string, cn), cC), cs).toString();
}

View File

@@ -0,0 +1,340 @@
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 'package:image/image.dart' as img;
import 'package:flutter_image_compress/flutter_image_compress.dart';
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 '../../main.dart';
import 'package:siro_service/controller/functions/encrypt_decrypt.dart';
import 'package:siro_service/env/env.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 driverId, String imageType) async {
final pickedImage = await picker.pickImage(
source: ImageSource.gallery,
);
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 rotatedImage = await rotateImage(compressedImage);
File processedImage = await rotateImageIfNeeded(File(croppedFile!.path));
File compressedImage = await compressImage(processedImage);
print('link =$link');
try {
await uploadImage(
compressedImage,
{'driverID': driverId, '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), //'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),
);
String token = r(box.read(BoxName.jwt) ?? '').toString().split(Env.addd)[0];
request.headers.addAll({
'Cache-Control': 'no-cache, no-store, must-revalidate',
'Pragma': 'no-cache',
'Expires': '0',
'Authorization': 'Bearer $token',
'X-Device-FP': box.read(BoxName.fingerPrint)?.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}');
}
}
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),
);
String token = r(box.read(BoxName.jwt) ?? '').toString().split(Env.addd)[0];
request.headers.addAll({
'Authorization': 'Bearer $token',
'X-Device-FP': box.read(BoxName.fingerPrint)?.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);
}

View File

@@ -0,0 +1,43 @@
import 'dart:convert';
import 'package:jwt_decoder/jwt_decoder.dart';
import 'package:secure_string_operations/secure_string_operations.dart';
import 'package:siro_service/controller/functions/crud.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';
class AppInitializer {
List<Map<String, dynamic>> links = [];
// Future<void> initializeApp() async {
// if (box.read(BoxName.jwt) == null) {
// await CRUD().getJWT();
// } else {
// bool isTokenExpired = JwtDecoder.isExpired(X
// .r(X.r(X.r(box.read(BoxName.jwt), cn), cC), cs)
// .toString()
// .split(AppInformation.addd)[0]);
// if (isTokenExpired) {
// await CRUD().getJWT();
// }
// }
// }
getAIKey(String key1) async {
if (box.read(BoxName.firstTimeLoadKey) == null) {
var res =
await CRUD().get(link: Env.getapiKey, payload: {"keyName": key1});
if (res != 'failure') {
var d = res['message'];
await storage.write(key: key1, value: d[key1].toString());
await Future.delayed(Duration.zero);
} else {}
}
}
}

View File

@@ -0,0 +1,76 @@
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=${Uri.encodeComponent(message)}';
break;
case 'whatsapp':
url =
'https://api.whatsapp.com/send?phone=$contactInfo&text=${Uri.encodeComponent(message)}';
break;
case 'email':
url =
'mailto:$contactInfo?subject=Subject&body=${Uri.encodeComponent(message)}';
break;
default:
return;
}
} else if (Platform.isAndroid) {
switch (method) {
case 'phone':
url = 'tel:$contactInfo';
break;
case 'sms':
url = 'sms:$contactInfo?body=${Uri.encodeComponent(message)}';
break;
case 'whatsapp':
// Check if WhatsApp is installed
final bool whatsappInstalled =
await canLaunchUrl(Uri.parse('whatsapp://'));
if (whatsappInstalled) {
url =
'whatsapp://send?phone=$contactInfo&text=${Uri.encodeComponent(message)}';
} else {
// Provide an alternative action, such as opening the WhatsApp Web API
url =
'https://api.whatsapp.com/send?phone=$contactInfo&text=${Uri.encodeComponent(message)}';
}
break;
case 'email':
url =
'mailto:$contactInfo?subject=Subject&body=${Uri.encodeComponent(message)}';
break;
default:
return;
}
} else {
return;
}
if (await canLaunchUrl(Uri.parse(url))) {
await launchUrl(Uri.parse(url));
} else {}
}

View File

@@ -0,0 +1,32 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
import 'package:siro_service/print.dart';
class SecurityHelper {
static const platform = MethodChannel('com.service_intaleq/security');
static Future<String?> getAppSignature() async {
try {
final String? signature = await platform.invokeMethod('getAppSignature');
final mode = kDebugMode ? 'DEBUG' : 'RELEASE';
Log.print('----------------------------------------------------');
Log.print('🚀 APP SIGNATURE HASH ($mode): $signature');
Log.print('----------------------------------------------------');
return signature;
} on PlatformException catch (e) {
Log.print('❌ Failed to get app signature: ${e.message}');
return null;
}
}
static Future<bool> isDeviceRooted() async {
try {
final bool isRooted = await platform.invokeMethod('isNativeRooted');
return isRooted;
} on PlatformException catch (e) {
Log.print('❌ Failed to check root: ${e.message}');
return false;
}
}
}

View File

@@ -0,0 +1,102 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../../constant/box_name.dart';
import '../../main.dart';
import '../themes/themes.dart';
class LocaleController extends GetxController {
Locale? language;
String countryCode = '';
ThemeData appTheme = lightThemeEnglish;
void changeLang(String langcode) {
Locale locale;
switch (langcode) {
case "ar":
locale = const Locale("ar");
appTheme = lightThemeArabic;
box.write(BoxName.lang, 'ar');
break;
case "en":
locale = const Locale("en");
appTheme = lightThemeEnglish;
box.write(BoxName.lang, 'en');
break;
case "tr":
locale = const Locale("tr");
appTheme = lightThemeEnglish;
box.write(BoxName.lang, 'tr');
break;
case "fr":
locale = const Locale("fr");
appTheme = lightThemeEnglish;
box.write(BoxName.lang, 'fr');
break;
case "it":
locale = const Locale("it");
appTheme = lightThemeEnglish;
box.write(BoxName.lang, 'it');
break;
case "de":
locale = const Locale("de");
appTheme = lightThemeEnglish;
box.write(BoxName.lang, 'de');
break;
case "el":
locale = const Locale("el");
appTheme = lightThemeEnglish;
box.write(BoxName.lang, 'el');
break;
case "es":
locale = const Locale("es");
appTheme = lightThemeEnglish;
box.write(BoxName.lang, 'es');
break;
case "fa":
locale = const Locale("fa");
appTheme = lightThemeEnglish;
box.write(BoxName.lang, 'fa');
break;
case "zh":
locale = const Locale("zh");
appTheme = lightThemeEnglish;
box.write(BoxName.lang, 'zh');
break;
case "ru":
locale = const Locale("ru");
appTheme = lightThemeEnglish;
box.write(BoxName.lang, 'ru');
break;
case "hi":
locale = const Locale("hi");
appTheme = lightThemeEnglish;
box.write(BoxName.lang, 'hi');
break;
default:
locale = Locale(Get.deviceLocale!.languageCode);
box.write(BoxName.lang, Get.deviceLocale!.languageCode);
appTheme = lightThemeEnglish;
break;
}
box.write(BoxName.lang, langcode);
Get.changeTheme(appTheme);
Get.updateLocale(locale);
update();
}
@override
void onInit() {
String? storedLang = box.read(BoxName.lang);
if (storedLang == null) {
// Use device language if no language is stored
storedLang = Get.deviceLocale!.languageCode;
box.write(BoxName.lang, storedLang);
}
changeLang(storedLang);
super.onInit();
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,330 @@
import 'dart:async';
import 'dart:io';
import 'package:get/get.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:timezone/data/latest.dart' as tz;
import 'package:timezone/timezone.dart' as tz;
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import '../../main.dart';
class NotificationController extends GetxController {
final FlutterLocalNotificationsPlugin _flutterLocalNotificationsPlugin =
FlutterLocalNotificationsPlugin();
@override
void onInit() {
super.onInit();
initNotifications();
}
// Initializes the local notifications plugin
Future<void> initNotifications() async {
const AndroidInitializationSettings android =
AndroidInitializationSettings('@mipmap/launcher_icon');
DarwinInitializationSettings ios = DarwinInitializationSettings(
requestAlertPermission: true,
requestBadgePermission: true,
requestSoundPermission: true,
// onDidReceiveLocalNotification:
// (int id, String? title, String? body, String? payload) async {},
);
InitializationSettings initializationSettings =
InitializationSettings(android: android, iOS: ios);
await _flutterLocalNotificationsPlugin.initialize(initializationSettings);
tz.initializeTimeZones();
print('Notifications initialized');
}
// Displays a notification with the given title and message
void showNotification(String title, String message, String tone) async {
final AndroidNotificationDetails android = AndroidNotificationDetails(
'high_importance_channel',
'High Importance Notifications',
importance: Importance.max,
priority: Priority.high,
showWhen: false,
sound: RawResourceAndroidNotificationSound(tone),
);
const DarwinNotificationDetails ios = DarwinNotificationDetails(
sound: 'default',
presentAlert: true,
presentBadge: true,
presentSound: true,
);
final NotificationDetails details =
NotificationDetails(android: android, iOS: ios);
await _flutterLocalNotificationsPlugin.show(0, title, message, details);
print('Notification shown: $title - $message');
}
// /Users/hamzaaleghwairyeen/development/App/ride 2/lib/controller/firebase/local_notification.dart
// Assume _flutterLocalNotificationsPlugin is initialized somewhere in your code
// void scheduleNotificationsForSevenDays(
// String title, String message, String tone) async {
// final AndroidNotificationDetails android = AndroidNotificationDetails(
// 'high_importance_channel',
// 'High Importance Notifications',
// importance: Importance.max,
// priority: Priority.high,
// sound: RawResourceAndroidNotificationSound(tone),
// );
// const DarwinNotificationDetails ios = DarwinNotificationDetails(
// sound: 'default',
// presentAlert: true,
// presentBadge: true,
// presentSound: true,
// );
// final NotificationDetails details =
// NotificationDetails(android: android, iOS: ios);
// // Check for the exact alarm permission on Android 12 and above
// if (Platform.isAndroid) {
// if (await Permission.scheduleExactAlarm.isDenied) {
// if (await Permission.scheduleExactAlarm.request().isGranted) {
// print('SCHEDULE_EXACT_ALARM permission granted');
// } else {
// print('SCHEDULE_EXACT_ALARM permission denied');
// return;
// }
// }
// }
// // Schedule notifications for the next 7 days
// for (int day = 0; day < 7; day++) {
// // Schedule for 8:00 AM
// await _scheduleNotificationForTime(
// day, 8, 0, title, message, details, day * 1000 + 1);
// // Schedule for 3:00 PM
// await _scheduleNotificationForTime(
// day, 15, 0, title, message, details, day * 1000 + 2); // Unique ID
// // Schedule for 8:00 PM
// await _scheduleNotificationForTime(
// day, 20, 0, title, message, details, day * 1000 + 3); // Unique ID
// }
// print('Notifications scheduled successfully for the next 7 days');
// }
void scheduleNotificationsForSevenDays(
String title, String message, String tone) async {
final AndroidNotificationDetails android = AndroidNotificationDetails(
'high_importance_channel',
'High Importance Notifications',
importance: Importance.max,
priority: Priority.high,
sound: RawResourceAndroidNotificationSound(tone),
);
const DarwinNotificationDetails ios = DarwinNotificationDetails(
sound: 'default',
presentAlert: true,
presentBadge: true,
presentSound: true,
);
final NotificationDetails details =
NotificationDetails(android: android, iOS: ios);
// Check for the exact alarm permission on Android 12 and above
if (Platform.isAndroid) {
if (await Permission.scheduleExactAlarm.isDenied) {
if (await Permission.scheduleExactAlarm.request().isGranted) {
print('SCHEDULE_EXACT_ALARM permission granted');
} else {
print('SCHEDULE_EXACT_ALARM permission denied');
return;
}
}
}
// Schedule notifications for the next 7 days
for (int day = 0; day < 7; day++) {
// List of notification times
final notificationTimes = [
{'hour': 8, 'minute': 0, 'id': day * 1000 + 1}, // 8:00 AM
{'hour': 15, 'minute': 0, 'id': day * 1000 + 2}, // 3:00 PM
{'hour': 20, 'minute': 0, 'id': day * 1000 + 3}, // 8:00 PM
];
for (var time in notificationTimes) {
final notificationId = time['id'] as int;
// Check if this notification ID is already stored
bool isScheduled = box.read('notification_$notificationId') ?? false;
if (!isScheduled) {
// Schedule the notification if not already scheduled
await _scheduleNotificationForTime(
day,
time['hour'] as int,
time['minute'] as int,
title,
message,
details,
notificationId,
);
// Mark this notification ID as scheduled in GetStorage
box.write('notification_$notificationId', true);
} else {
print('Notification with ID $notificationId is already scheduled.');
}
}
}
print('Notifications scheduled successfully for the next 7 days');
}
void scheduleNotificationsForTimeSelected(
String title, String message, String tone, DateTime timeSelected) async {
final AndroidNotificationDetails android = AndroidNotificationDetails(
'high_importance_channel',
'High Importance Notifications',
importance: Importance.max,
priority: Priority.high,
sound: RawResourceAndroidNotificationSound(tone),
);
const DarwinNotificationDetails ios = DarwinNotificationDetails(
sound: 'default',
presentAlert: true,
presentBadge: true,
presentSound: true,
);
final NotificationDetails details =
NotificationDetails(android: android, iOS: ios);
// Check for the exact alarm permission on Android 12 and above
if (Platform.isAndroid) {
if (await Permission.scheduleExactAlarm.isDenied) {
if (await Permission.scheduleExactAlarm.request().isGranted) {
print('SCHEDULE_EXACT_ALARM permission granted');
} else {
print('SCHEDULE_EXACT_ALARM permission denied');
return;
}
}
}
// Schedule notifications for 10 and 30 minutes before the timeSelected
await _scheduleNotificationForTimeVIP(
timeSelected.subtract(const Duration(minutes: 10)), // 10 minutes before
title,
message,
details,
1, // Unique ID for 10-minute before notification
);
await _scheduleNotificationForTimeVIP(
timeSelected.subtract(const Duration(minutes: 30)), // 30 minutes before
title,
message,
details,
2, // Unique ID for 30-minute before notification
);
print('Notifications scheduled successfully for the time selected');
}
Future<void> _scheduleNotificationForTimeVIP(
DateTime scheduledDate,
String title,
String message,
NotificationDetails details,
int notificationId,
) async {
// Initialize and set Cairo timezone
tz.initializeTimeZones();
var cairoLocation = tz.getLocation('Africa/Cairo');
final now = tz.TZDateTime.now(cairoLocation);
// Convert to Cairo time
tz.TZDateTime scheduledTZDateTime =
tz.TZDateTime.from(scheduledDate, cairoLocation);
// Check if 10 minutes before the scheduled time is in the past
if (scheduledTZDateTime
.subtract(const Duration(minutes: 10))
.isBefore(now)) {
// If the 10 minutes before the scheduled time is in the past, don't schedule
print(
'Scheduled time minus 10 minutes is in the past. Skipping notification.');
return; // Skip this notification
}
print('Current time (Cairo): $now');
print('Scheduling notification for: $scheduledTZDateTime');
await _flutterLocalNotificationsPlugin.zonedSchedule(
notificationId, // Unique ID for each notification
title,
message,
scheduledTZDateTime,
details,
androidScheduleMode: AndroidScheduleMode.exact,
// uiLocalNotificationDateInterpretation:
// UILocalNotificationDateInterpretation.absoluteTime,
matchDateTimeComponents:
null, // Don't repeat automatically; we handle manually
);
print('Notification scheduled successfully for: $scheduledTZDateTime');
}
Future<void> _scheduleNotificationForTime(
int dayOffset,
int hour,
int minute,
String title,
String message,
NotificationDetails details,
int notificationId,
) async {
// Initialize and set Cairo timezone
tz.initializeTimeZones();
var cairoLocation = tz.getLocation('Africa/Cairo');
final now = tz.TZDateTime.now(cairoLocation);
tz.TZDateTime scheduledDate = tz.TZDateTime(
cairoLocation,
now.year,
now.month,
now.day + dayOffset, // Add offset to schedule for the next days
hour,
minute,
);
// If the scheduled time is in the past, move it to the next day
if (scheduledDate.isBefore(now)) {
scheduledDate = scheduledDate.add(const Duration(days: 1));
}
print('Current time (Cairo): $now');
print('Scheduling notification for: $scheduledDate');
await _flutterLocalNotificationsPlugin.zonedSchedule(
notificationId, // Unique ID for each notification
title,
message,
scheduledDate,
details,
androidScheduleMode: AndroidScheduleMode.exact,
// uiLocalNotificationDateInterpretation:
// UILocalNotificationDateInterpretation.absoluteTime,
matchDateTimeComponents:
null, // Don't repeat automatically; we handle 7 days manually
);
print('Notification scheduled successfully for: $scheduledDate');
}
}

View File

@@ -0,0 +1,89 @@
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:siro_service/constant/links.dart';
import 'package:siro_service/controller/functions/crud.dart';
import 'package:siro_service/controller/functions/device_helper.dart';
import 'package:siro_service/controller/functions/encrypt_decrypt.dart';
import '../constant/box_name.dart';
import '../main.dart';
import '../print.dart';
import '../views/home/main.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
class LoginController extends GetxController {
var email = TextEditingController(); // Kept for UI compatibility
var password = TextEditingController();
final formKey = GlobalKey<FormState>();
final FlutterSecureStorage storage = const FlutterSecureStorage();
void login() async {
// Ensure fingerprint is ready
String fingerprint = box.read(BoxName.fingerPrint) ?? '';
if (fingerprint.isEmpty) {
fingerprint = await DeviceHelper.getDeviceFingerprint();
}
String? storedPassword = await storage.read(key: 'password');
String pass = storedPassword ?? password.text;
if (pass.isEmpty && !formKey.currentState!.validate()) return;
var payload = {
"fingerprint": fingerprint,
"password": pass,
"aud": "service",
};
Log.print('🚀 Login Payload: $payload');
var res = await CRUD().post(link: AppLink.login, payload: payload);
Log.print('📥 Login Response: $res');
if (res != 'failure' && res is Map && res['status'] == 'success') {
var d = res[
'message']; // V1 returns {status, message: {jwt, data: {user...}}}
// Store JWT & HMAC
final jwt = d['jwt'];
final hmac = d['hmac'];
await box.write(BoxName.jwt, c(jwt));
if (hmac != null) {
await box.write(BoxName.hmac, hmac);
}
// Store User Data
var userData = d['data'];
await storage.write(key: 'name', value: userData['first_name']);
await storage.write(key: 'driverID', value: userData['id'].toString());
await storage.write(key: 'password', value: pass);
await box.write(BoxName.employeename, userData['first_name']);
await box.write(BoxName.password, pass);
Get.offAll(() => Main());
} else {
Get.snackbar(
'خطأ'.tr,
res is Map ? res['message'].toString().tr : 'فشل تسجيل الدخول'.tr,
backgroundColor: Colors.red.withOpacity(0.7),
colorText: Colors.white,
);
}
}
@override
void onInit() async {
await EncryptionHelper.initialize();
await DeviceHelper.getDeviceFingerprint();
// Auto login if credentials exist
String? storedPassword = await storage.read(key: 'password');
if (storedPassword != null) {
login();
}
super.onInit();
}
}

View File

@@ -0,0 +1,567 @@
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:siro_service/constant/colors.dart';
import 'package:siro_service/constant/links.dart';
import 'package:siro_service/controller/functions/crud.dart';
import 'package:siro_service/controller/mainController/pages/driver_page.dart';
import 'package:siro_service/main.dart';
import 'package:siro_service/views/widgets/my_dialog.dart';
import 'package:url_launcher/url_launcher.dart';
import '../../print.dart';
import 'pages/passengers_page.dart';
class MainController extends GetxController {
final formKey = GlobalKey<FormState>();
bool isLoading = false;
final passengerPhoneController = TextEditingController();
final driverPhoneController = TextEditingController();
final notesController = TextEditingController();
final carplateController = TextEditingController();
TextEditingController colorController = TextEditingController();
TextEditingController makeController = TextEditingController();
TextEditingController modelController = TextEditingController();
TextEditingController expirationDateController = TextEditingController();
TextEditingController yearController = TextEditingController();
TextEditingController ownerController = TextEditingController();
TextEditingController carOwnerWorkController = TextEditingController();
TextEditingController driverNameController = TextEditingController();
TextEditingController nationalIdController = TextEditingController();
TextEditingController birthDateController = TextEditingController();
TextEditingController licenseTypeController = TextEditingController();
TextEditingController phoneController = TextEditingController();
TextEditingController phoneCarController = TextEditingController();
TextEditingController carNumberController = TextEditingController();
TextEditingController manufactureYearController = TextEditingController();
TextEditingController carModelController = TextEditingController();
TextEditingController carTypeController = TextEditingController();
TextEditingController siteCarController = TextEditingController();
TextEditingController siteDriverController = TextEditingController();
TextEditingController registrationDateController = TextEditingController();
Map passengerData = {};
Map driverData = {};
List filteredDrivers = [];
var color = ''.obs;
var colorHex = ''.obs;
@override
void onInit() {
super.onInit();
// refreshDashboardStats(); // Removed to save data consumption at start
}
Future<void> refreshDashboardStats() async {
isLoading = true;
update();
try {
await Future.wait<void>([
getDriverWantCompleteRegistration(),
getDriverNotCompleteRegistration(),
getNewDriverRegister(),
]);
} catch (e) {
Log.print('Error refreshing stats: $e');
}
isLoading = false;
update();
}
Future<void> searchPassengerByPhone() async {
if (formKey.currentState == null || formKey.currentState!.validate()) {
isLoading = true;
update();
await getPassengersByPhone();
isLoading = false;
update();
Get.back();
if (passengerData.isEmpty) {
Get.snackbar('Error'.tr, 'Passenger not found'.tr,
backgroundColor: Colors.red, colorText: Colors.white);
return;
}
Get.to(() => PassengersPage());
}
}
void searchDrivers(String query) {
if (query.isEmpty) {
filteredDrivers = driverNotCompleteRegistration;
update();
} else {
filteredDrivers = driverNotCompleteRegistration
.where((driver) => driver['phone_number']
.toString()
.toLowerCase()
.contains(query.toLowerCase()))
.toList();
update();
}
}
void updateDriverField(String key, dynamic value) async {
// Update locally
driverData['message'][0][key] = value;
Log.print('driverData: ${driverData['message'][0]['driverID']}');
update();
var res = await CRUD().post(link: AppLink.updateDriver, payload: {
'driverID': driverData['message'][0]['driverID'].toString(),
key: value.toString(),
});
if (res == 'failure') {
Get.snackbar('Error', 'Failed to update driver data',
backgroundColor: AppColor.redColor);
} else {
Get.snackbar('Success', 'Driver data updated successfully',
backgroundColor: AppColor.greenColor);
}
// Optionally fetch driver again
// await getDriverData();
}
Future<void> makePhoneCall(String phoneNumber) async {
final Uri launchUri = Uri(
scheme: 'tel',
path: phoneNumber,
);
await launchUrl(launchUri);
}
Future<void> launchCommunication(
String method, String contactInfo, String message) async {
// رقّم فقط (بدون + أو مسافات)
final phone = contactInfo.replaceAll(RegExp(r'[^0-9]'), '');
final encodedMsg = Uri.encodeComponent(message);
Uri? uri;
if (Platform.isIOS) {
switch (method) {
case 'phone':
uri = Uri.parse('tel:$phone');
break;
case 'sms':
uri = Uri.parse('sms:$phone?body=$encodedMsg');
break;
case 'whatsapp':
uri = Uri.parse(
'https://api.whatsapp.com/send?phone=$phone&text=$encodedMsg');
break;
case 'email':
uri =
Uri.parse('mailto:$contactInfo?subject=Subject&body=$encodedMsg');
break;
default:
return;
}
} else if (Platform.isAndroid) {
switch (method) {
case 'phone':
uri = Uri.parse('tel:$phone');
break;
case 'sms':
uri = Uri.parse('sms:$phone?body=$encodedMsg');
break;
case 'whatsapp':
{
final waDeepLink =
Uri.parse('whatsapp://send?phone=$phone&text=$encodedMsg');
if (await canLaunchUrl(waDeepLink)) {
await launchUrl(waDeepLink, mode: LaunchMode.externalApplication);
return;
} else {
final webUri = Uri.parse(
'https://api.whatsapp.com/send?phone=$phone&text=$encodedMsg');
if (await canLaunchUrl(webUri)) {
await launchUrl(webUri, mode: LaunchMode.externalApplication);
return;
}
// لو ما في متصفح أساسًا
throw 'No handler for WhatsApp links';
}
}
case 'email':
uri =
Uri.parse('mailto:$contactInfo?subject=Subject&body=$encodedMsg');
break;
default:
return;
}
} else {
return;
}
final ok = await canLaunchUrl(uri);
if (ok) {
await launchUrl(uri, mode: LaunchMode.externalApplication);
} else {
// ممكن تضيف Snackbar/Toast هنا
}
}
List driverNotCompleteRegistration = [];
Future<void> getDriverNotCompleteRegistration() async {
var res = await CRUD()
.get(link: AppLink.getDriverNotCompleteRegistration, payload: {});
if (res != 'failure') {
var d = res['message'];
driverNotCompleteRegistration = d;
filteredDrivers = driverNotCompleteRegistration;
update();
} else {
driverNotCompleteRegistration = [];
update();
}
}
Future<void> deleteDriverNotCompleteRegistration(String phone) async {
var res = await CRUD()
.get(link: AppLink.deleteDriverNotCompleteRegistration, payload: {
'phone': phone,
});
if (res != 'failure') {
Get.snackbar(res, '', backgroundColor: AppColor.greenColor);
// await getDriverWantCompleteRegistration();
update();
} else {
Get.snackbar(res, '');
}
}
List driverWantCompleteRegistration = [];
Future<void> getDriverWantCompleteRegistration() async {
var res =
await CRUD().get(link: AppLink.getDriversWaitingActive, payload: {});
if (res != 'failure') {
var d = res['message'];
driverWantCompleteRegistration = d;
filteredDrivers = driverWantCompleteRegistration;
update();
} else {
driverWantCompleteRegistration = [];
update();
}
}
List driversPhoneNotComplete = [];
Future<void> getDriversPhoneNotComplete() async {
var res =
await CRUD().get(link: AppLink.getDriversPhoneNotComplete, payload: {});
if (res != 'failure') {
var d = res['message'];
driverWantCompleteRegistration = d;
filteredDrivers = driverWantCompleteRegistration;
update();
} else {
Get.snackbar(res, '');
}
}
List newDriverRegister = [];
Future<void> getNewDriverRegister() async {
var res = await CRUD().get(link: AppLink.getNewDriverRegister, payload: {});
if (res != 'failure') {
var d = res['message'];
newDriverRegister = d;
update();
} else {
newDriverRegister = [];
update();
}
}
Future<void> addWelcomeCall(String driveId) async {
var res = await CRUD().post(link: AppLink.addWelcomeDriverNote, payload: {
"driverId": driveId,
"notes": notesController.text,
});
if (res != 'failure') {
Get.snackbar('Success'.tr, '', backgroundColor: AppColor.greenColor);
}
}
String selectedStatus = "I'm not ready yet".tr;
List passengerNotCompleteRegistration = [];
Future<void> getPassengerNotCompleteRegistration() async {
var res = await CRUD()
.get(link: AppLink.getPassengersNotCompleteRegistration, payload: {});
if (res != 'failure') {
var d = res['message'];
passengerNotCompleteRegistration = d;
update();
} else {
Get.snackbar(res, '');
}
}
void setSelectedStatus(String status) {
selectedStatus = status;
update();
}
final List<String> statusOptions = [
"I'm not ready yet".tr,
"I don't have a suitable vehicle".tr,
"I'll register when the app is fully launched".tr,
"I need more help understanding the app".tr,
"My documents have expired".tr,
];
List carPlateNotEdit = [];
getCarPlateNotEdit() async {
var res = await CRUD().get(link: AppLink.getCarPlateNotEdit, payload: {});
if (res != 'failure') {
var d = res['message'];
carPlateNotEdit = d;
update();
} else {
MyDialog().getDialog('No Car found yet'.tr, 'thanks'.tr, const SizedBox(),
() {
Get.back();
});
}
}
List driverWithoutCar = [];
getdriverWithoutCar() async {
var res = await CRUD().get(link: AppLink.getdriverWithoutCar, payload: {});
if (res != 'failure') {
var d = res['message'];
driverWithoutCar = d;
update();
} else {
MyDialog().getDialog('No Car found yet'.tr, 'thanks'.tr, const SizedBox(),
() {
Get.back();
});
}
}
Future<void> addRegistrationCarEgyptHandling({
required String driverId,
required String carPlate,
required String color,
required String colorHex,
required String year,
required String make,
required String model,
required String expirationDate,
required String owner,
}) async {
try {
isLoading = true;
update();
var payload = {
'driverID': driverId,
'vin': 'vin',
'car_plate': carPlate,
'make': make,
'model': model,
'year': year,
'expiration_date': expirationDate,
'color': color,
'owner': owner,
'color_hex': colorHex,
'address': 'addressCar',
'displacement': 'displacement',
'fuel': 'fuel',
'registration_date': '2024-09-06',
};
Log.print('Payload: $payload');
var res =
await CRUD().post(link: AppLink.addCartoDriver, payload: payload);
isLoading = false;
update();
var status = res;
Log.print('res: $res');
Log.print('status: $status');
if (status != 'failure' && status['status'] == 'success') {
await Future.wait([
CRUD().post(
link:
'${AppLink.seferAlexandriaServer}/ride/RegisrationCar/add.php',
payload: payload),
CRUD().post(
link: '${AppLink.seferGizaServer}/ride/RegisrationCar/add.php',
payload: payload),
]);
Get.snackbar('Success', 'Registration successful',
backgroundColor: AppColor.greenColor);
Get.back();
} else {
Log.print('Error: Unexpected status: ${status['status']}');
Get.snackbar('Error', 'Registration failed',
backgroundColor: Colors.red);
}
} catch (e) {
Log.print('Error: $e');
Get.snackbar('Error', 'An error occurred during registration',
backgroundColor: Colors.red);
}
}
editCarPlateNotEdit(
String driverId,
String carPlate,
String color,
String colorHex,
String year,
String make,
String model,
String expirationDate,
String owner,
) async {
var res = await CRUD().post(link: AppLink.editCarPlate, payload: {
"driverId": driverId,
"carPlate": carPlate,
"color": color,
"color_hex": colorHex,
"make": make,
"year": year,
"model": model,
"expiration_date": expirationDate.toString(),
"owner": owner,
"employee": storage.read(key: 'name').toString(),
});
Log.print('res: ${res}');
if (res != 'failure') {
// Get.snackbar(res, '', backgroundColor: AppColor.greenColor);
Get.back();
carplateController.clear();
yearController.clear();
makeController.clear();
modelController.clear();
ownerController.clear();
await getCarPlateNotEdit();
update();
} else {
Get.snackbar(res, '', backgroundColor: AppColor.redColor);
}
}
// editCarPlateNotEdit(String driverId, carPlate) async {
// var res = await CRUD().post(link: AppLink.editCarPlate, payload: {
// "driverId": driverId,
// "carPlate": carPlate,
// });
// if (res != 'failure') {
// Get.snackbar(res, '', backgroundColor: AppColor.greenColor);
// carplateController.clear();
// await getCarPlateNotEdit();
// update();
// } else {
// Get.snackbar(res, '', backgroundColor: AppColor.redColor);
// }
// }
saveNoteForDriverNotCompleteRegistration(String phone, editor, note) async {
var res = await CRUD().post(
link: AppLink.addNotesDriver,
payload: {"phone": phone, "editor": editor, "note": note});
if (res != 'failure') {
Get.snackbar(res, '', backgroundColor: AppColor.greenColor);
getDriversPhoneNotComplete();
notesController.clear();
} else {
Get.snackbar(res, '', backgroundColor: AppColor.redColor);
}
}
saveNoteForPassengerNotCompleteRegistration(
String phone, editor, note) async {
var res = await CRUD().post(
link: AppLink.addNotesPassenger,
payload: {"phone": phone, "editor": editor, "note": note});
if (res != 'failure') {
Get.snackbar(res, '', backgroundColor: AppColor.greenColor);
notesController.clear();
} else {
Get.snackbar(res, '', backgroundColor: AppColor.redColor);
}
}
searchDriverByPhone() async {
if (formKey.currentState == null || formKey.currentState!.validate()) {
isLoading = true;
update();
await getDriverByPhone();
isLoading = false;
update();
Get.back();
if (driverData.isEmpty) {
Get.snackbar('Error'.tr, 'Driver not found'.tr,
backgroundColor: Colors.red, colorText: Colors.white);
return;
}
Get.to(() => DriverPage());
}
}
searchDriverByNational() async {
if (formKey.currentState == null || formKey.currentState!.validate()) {
isLoading = true;
update();
await getDriverByNational();
isLoading = false;
update();
Get.back();
if (driverData.isEmpty) {
Get.snackbar('Error'.tr, 'Driver not found'.tr,
backgroundColor: Colors.red, colorText: Colors.white);
return;
}
Get.to(() => DriverPage());
}
}
getPassengersByPhone() async {
var res = await CRUD().get(
link: AppLink.getPassengersByPhone,
payload: {"phone": passengerPhoneController.text});
if (res != 'failure') {
var d = res;
passengerData = d;
update();
}
}
getDriverByPhone() async {
var res = await CRUD().get(
link: AppLink.getDriverByPhone,
payload: {"phone": driverPhoneController.text});
if (res != 'failure') {
var d = res;
driverData = d;
update();
} else {
Get.snackbar('Error', 'Driver not found', backgroundColor: Colors.red);
}
}
getDriverByNational() async {
var res = await CRUD().get(
link: AppLink.getDriverByNational,
payload: {"national_number": driverPhoneController.text});
if (res != 'failure') {
var d = res;
driverData = d;
update();
} else {
Get.snackbar('Error', 'Driver not found', backgroundColor: Colors.red);
}
}
}

View File

@@ -0,0 +1,378 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:intl/intl.dart';
import 'package:siro_service/constant/style.dart';
import 'package:siro_service/controller/functions/launch.dart';
import 'package:siro_service/views/widgets/my_scafold.dart';
import '../../../constant/colors.dart';
import '../../../constant/links.dart';
import '../../../views/widgets/my_textField.dart';
import '../../functions/encrypt_decrypt.dart';
import '../../functions/image.dart';
import '../main_controller.dart';
class AddCar extends StatelessWidget {
const AddCar({super.key});
@override
Widget build(BuildContext context) {
Get.put(MainController());
return GetBuilder<MainController>(builder: (mainController) {
return MyScaffold(
title: 'Edit car details'.tr,
isleading: true,
body: [
Expanded(
child: ListView.builder(
padding: const EdgeInsets.all(16.0),
itemCount: mainController
.driverWithoutCar.length, // 10 fields + 1 save button
itemBuilder: (context, index) {
var carData = mainController.driverWithoutCar[index];
return Padding(
padding: const EdgeInsets.all(8.0),
child: InkWell(
onTap: () {
Get.to(AddCarForm(carData: carData));
},
child: Container(
decoration: AppStyle.boxDecoration1,
child: Text((carData['name_arabic']))),
),
);
}),
),
],
);
});
}
}
class AddCarForm extends StatelessWidget {
final Map carData;
const AddCarForm({super.key, required this.carData});
@override
Widget build(BuildContext context) {
Get.put(MainController());
return GetBuilder<MainController>(builder: (mainController) {
return MyScaffold(
title: 'Add Car',
action: Row(
children: [
IconButton(
onPressed: () {
makePhoneCall(carData['phone']);
},
icon: const Icon(Icons.phone),
),
IconButton(
onPressed: () {
launchCommunication('whatsapp', carData['phone'], '');
},
icon: const Icon(
Icons.message,
color: AppColor.greenColor,
),
),
],
),
isleading: true,
body: [
ListView(
children: [
Column(
children: [
GestureDetector(
onLongPress: () async {
await ImageController().choosImage(
AppLink.uploadEgypt, carData['id'], 'car_front');
},
child: Image.network(
'${AppLink.server}/card_image/car_front-${carData['id']}.jpg',
height: 200,
width: double.maxFinite,
fit: BoxFit.fill,
errorBuilder: (BuildContext context, Object exception,
StackTrace? stackTrace) {
// If the image fails to load, use the _copy version
return Image.network(
'${AppLink.server}/card_image/car_front-${carData['id']}_copy.jpg',
height: 200,
width: double.maxFinite,
fit: BoxFit.fill,
);
},
),
),
GestureDetector(
onLongPress: () async {
await ImageController().choosImage(
AppLink.uploadEgypt, carData['id'], 'car_back');
},
child: Image.network(
'${AppLink.server}/card_image/car_back-${carData['id']}.jpg',
height: 200,
width: double.maxFinite,
fit: BoxFit.fill,
errorBuilder: (BuildContext context, Object exception,
StackTrace? stackTrace) {
// If the image fails to load, use the _copy version
return Image.network(
'${AppLink.server}/card_image/car_back-${carData['id']}_copy.jpg',
height: 200,
width: double.maxFinite,
fit: BoxFit.fill,
);
},
),
),
],
),
const SizedBox(height: 9),
Form(
key: mainController.formKey,
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
SizedBox(
width: Get.width * .6,
child: MyTextForm(
controller: mainController.carplateController,
label: 'car plate'.tr,
hint: 'car plate'.tr,
type: TextInputType.name,
),
),
IconButton(
onPressed: () async {
if (mainController.formKey.currentState!
.validate()) {
await mainController
.addRegistrationCarEgyptHandling(
driverId: carData['id'].toString(),
carPlate:
mainController.carplateController.text,
color: mainController.colorController.text,
colorHex:
mainController.colorHex.value.toString(),
year: mainController.yearController.text,
make: mainController.makeController.text,
model: mainController.modelController.text,
expirationDate: mainController
.expirationDateController.text,
owner: mainController.ownerController.text,
);
}
},
icon: const Icon(
Icons.upload_outlined,
color: AppColor.blueColor,
),
),
],
),
// Other fields
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
SizedBox(
width: Get.width * .4,
child: MyTextForm(
controller: mainController.yearController,
label: 'Year'.tr,
hint: 'Year'.tr,
type: TextInputType.number,
),
),
SizedBox(
width: Get.width * .4,
child: DropdownButtonFormField<String>(
decoration: InputDecoration(
labelText: 'Color'.tr, // Localized label
),
value: mainController.colorHex.value.isEmpty
? null
: mainController.colorHex
.value, // Use the hex value as the current value
items: [
{'red'.tr: '#FF0000'},
{'green'.tr: '#008000'},
{'blue'.tr: '#0000FF'},
{'black'.tr: '#000000'},
{'white'.tr: '#FFFFFF'},
{'yellow'.tr: '#FFFF00'},
{'purple'.tr: '#800080'},
{'orange'.tr: '#FFA500'},
{'pink'.tr: '#FFC0CB'},
{'brown'.tr: '#A52A2A'},
{'gray'.tr: '#808080'},
{'cyan'.tr: '#00FFFF'},
{'magenta'.tr: '#FF00FF'},
{'lime'.tr: '#00FF00'},
{'indigo'.tr: '#4B0082'},
{'violet'.tr: '#EE82EE'},
{'gold'.tr: '#FFD700'},
{'silver'.tr: '#C0C0C0'},
{'teal'.tr: '#008080'},
{'navy'.tr: '#000080'},
].map((colorMap) {
String colorName = colorMap.keys.first;
String colorValue = colorMap.values.first;
return DropdownMenuItem<String>(
value: colorValue,
child: Text(colorName),
);
}).toList(),
onChanged: (value) {
if (value != null) {
// Find the selected color name based on the hex value
String selectedColorName = '';
for (var colorMap in [
{'red'.tr: '#FF0000'},
{'green'.tr: '#008000'},
{'blue'.tr: '#0000FF'},
{'black'.tr: '#000000'},
{'white'.tr: '#FFFFFF'},
{'yellow'.tr: '#FFFF00'},
{'purple'.tr: '#800080'},
{'orange'.tr: '#FFA500'},
{'pink'.tr: '#FFC0CB'},
{'brown'.tr: '#A52A2A'},
{'gray'.tr: '#808080'},
{'cyan'.tr: '#00FFFF'},
{'magenta'.tr: '#FF00FF'},
{'lime'.tr: '#00FF00'},
{'indigo'.tr: '#4B0082'},
{'violet'.tr: '#EE82EE'},
{'gold'.tr: '#FFD700'},
{'silver'.tr: '#C0C0C0'},
{'teal'.tr: '#008080'},
{'navy'.tr: '#000080'},
]) {
if (colorMap.values.first == value) {
selectedColorName = colorMap.keys.first;
break;
}
}
mainController.colorController.text =
selectedColorName;
mainController.colorHex.value = value;
}
},
),
),
],
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
SizedBox(
width: Get.width * .4,
child: MyTextForm(
controller: mainController.makeController,
label: 'Make'.tr,
hint: 'Make'.tr,
type: TextInputType.name,
),
),
SizedBox(
width: Get.width * .4,
child: MyTextForm(
controller: mainController.modelController,
label: 'Model'.tr,
hint: 'Model'.tr,
type: TextInputType.name,
),
),
],
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
SizedBox(
width: Get.width * .4,
child: TextField(
controller:
mainController.expirationDateController,
decoration: InputDecoration(
labelText: 'Expiration Date'.tr,
hintText: 'Expiration Date'.tr,
),
readOnly:
true, // Make the field read-only to prevent manual input
onTap: () async {
DateTime pickedDate =
DateTime.now(); // Declare the variable here
await showCupertinoModalPopup<void>(
context: context,
builder: (context) => Container(
height: 250,
color: Colors.white,
child: Column(
children: [
SizedBox(
height: 150,
child: CupertinoDatePicker(
initialDateTime: pickedDate,
minimumDate: DateTime(
1955), // Set the starting date
maximumDate: DateTime(
2034), // Set the ending date
mode: CupertinoDatePickerMode.date,
onDateTimeChanged:
(DateTime dateTime) {
pickedDate = dateTime;
},
),
),
CupertinoButton(
child: Text('Done'.tr),
onPressed: () {
String formattedDate =
DateFormat('yyyy-MM-dd')
.format(pickedDate);
mainController
.expirationDateController
.text =
formattedDate.toString();
Navigator.of(context).pop();
},
),
],
),
),
);
},
),
),
SizedBox(
width: Get.width * .4,
child: MyTextForm(
controller: mainController.ownerController,
label: 'Owner'.tr,
hint: 'Owner'.tr,
type: TextInputType.name,
),
),
],
),
],
),
)
],
)
]);
});
}
}

View File

@@ -0,0 +1,56 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:siro_service/views/widgets/my_scafold.dart';
import '../../best_driver_controllers.dart';
import '../../functions/encrypt_decrypt.dart';
class DriverTheBestAlexandria extends StatelessWidget {
const DriverTheBestAlexandria({super.key});
@override
Widget build(BuildContext context) {
Get.put(DriverTheBestAlexandriaController(), permanent: true);
return MyScaffold(
title: 'Alexandria'.tr,
body: [
GetBuilder<DriverTheBestAlexandriaController>(builder: (driverthebest) {
return driverthebest.driver.isNotEmpty
? ListView.builder(
itemCount: driverthebest.driver.length,
itemBuilder: (context, index) {
final driver = driverthebest.driver[index];
return ListTile(
leading: CircleAvatar(
child: Text(
(int.parse(driver['driver_count'] * 5) / 3600)
.toStringAsFixed(0),
),
),
title: Text((driver['name_arabic']) ??
'Unknown Name'),
subtitle: Text(
'Phone: ${(driver['phone']) ?? 'N/A'}'),
trailing: IconButton(
onPressed: () async {
Get.defaultDialog(
title:
'are you sure to pay to this driver gift'.tr,
middleText: '',
onConfirm: () async {},
onCancel: () => Get.back());
},
icon: const Icon(Icons.wallet_giftcard_rounded),
),
);
},
)
: const Center(
child: Text('No drivers available.'),
);
})
],
isleading: true,
);
}
}

View File

@@ -0,0 +1,111 @@
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:siro_service/controller/functions/encrypt_decrypt.dart';
import 'package:siro_service/views/widgets/my_scafold.dart';
import '../../../constant/colors.dart';
import '../../../constant/links.dart';
import '../../../views/widgets/elevated_btn.dart';
import '../../functions/crud.dart';
import 'alexandria_besr_driver.dart';
import 'giza_best_driver.dart';
class DriverTheBest extends StatelessWidget {
const DriverTheBest({super.key});
@override
Widget build(BuildContext context) {
Get.put(Driverthebest());
return MyScaffold(
title: 'Best Drivers'.tr,
body: [
Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
MyElevatedButton(
title: 'Giza',
onPressed: () {
Get.to(() => DriverTheBestGiza());
}),
MyElevatedButton(
title: 'Alexandria',
onPressed: () {
Get.to(() => DriverTheBestAlexandria());
}),
],
),
GetBuilder<Driverthebest>(builder: (driverthebest) {
return driverthebest.driver.isNotEmpty
? SizedBox(
height: Get.height * .7,
child: ListView.builder(
itemCount: driverthebest.driver.length,
itemBuilder: (context, index) {
final driver = driverthebest.driver[index];
return ListTile(
leading: CircleAvatar(
child: Text(
((int.parse(driver['driver_count']) * 5) / 3600)
.toStringAsFixed(0),
),
),
title: Text((driver['name_arabic']) ??
'Unknown Name'),
subtitle: Text(
'Phone: ${(driver['phone']) ?? 'N/A'}'),
trailing: IconButton(
onPressed: () async {
// Get.defaultDialog(
// title:
// 'are you sure to pay to this driver gift'.tr,
// middleText: '',
// onConfirm: () async {
// // final wallet = Get.put(WalletController());
// // await wallet.addPaymentToDriver('100',
// // driver['id'].toString(), driver['token']);
// // await wallet.addSeferWallet(
// // '100', driver['id'].toString());
// },
// onCancel: () => Get.back());
},
icon: const Icon(Icons.wallet_giftcard_rounded),
),
);
},
),
)
: const Center(
child: Text('No drivers available.'),
);
}),
],
)
],
isleading: true,
);
}
}
class Driverthebest extends GetxController {
bool isLoading = false;
List driver = [];
getBestDriver() async {
var res = await CRUD().get(link: AppLink.getBestDriver, payload: {});
if (res != 'failure') {
driver = res['message'];
update();
} else {
Get.snackbar('error', '', backgroundColor: AppColor.redColor);
}
}
@override
void onInit() {
getBestDriver();
super.onInit();
}
}

View File

@@ -0,0 +1,12 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:siro_service/views/widgets/my_scafold.dart';
class Complaint extends StatelessWidget {
const Complaint({super.key});
@override
Widget build(BuildContext context) {
return MyScaffold(title: "View complaint".tr, isleading: true, body: []);
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,226 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../main_controller.dart';
class DriverPage extends StatelessWidget {
DriverPage({super.key});
final MainController mainController = Get.find<MainController>();
@override
Widget build(BuildContext context) {
return Scaffold(
body: GetBuilder<MainController>(builder: (mainController) {
Map data = mainController.driverData['message'][0];
return CupertinoPageScaffold(
navigationBar: CupertinoNavigationBar(
middle: Text('${data['first_name']} ${data['last_name']}'),
),
child: SafeArea(
child: CupertinoScrollbar(
child: ListView(
children: [
_buildDriverInfoSection(data),
_buildCommunicationSection(data, context),
_buildStatisticsSection(data),
_buildCarInfoSection(data),
_buildLicenseInfoSection(data),
_buildBankInfoSection(data),
const SizedBox(height: 40),
],
),
),
),
);
}),
);
}
// ============================================================
// REUSABLE EDIT ROW
// ============================================================
Widget _buildEditableRow(String label, String key, Map data) {
return CupertinoListTile(
title: Text(label),
trailing: Text(
data[key].toString(),
style: const TextStyle(color: CupertinoColors.systemGrey),
),
onTap: () => _openEditSheet(label, key, data[key]),
);
}
void _openEditSheet(String label, String key, dynamic value) {
final TextEditingController controller =
TextEditingController(text: value.toString());
Get.bottomSheet(
CupertinoPageScaffold(
navigationBar: CupertinoNavigationBar(
middle: Text("Edit $label"),
trailing: GestureDetector(
child: const Text(
"Save",
style: TextStyle(color: CupertinoColors.activeBlue),
),
onTap: () {
mainController.updateDriverField(key, controller.text);
Get.back();
},
),
),
child: SafeArea(
child: Padding(
padding: const EdgeInsets.all(16),
child: CupertinoTextField(
controller: controller,
autofocus: true,
),
),
),
),
);
}
// ============================================================
// SECTIONS
// ============================================================
Widget _buildDriverInfoSection(Map data) {
return CupertinoListSection.insetGrouped(
header: Text('Driver Information'.tr),
children: [
_buildEditableRow('Name Arabic'.tr, 'name_arabic', data),
// _buildEditableRow('Name English'.tr, 'name_english', data),
_buildEditableRow('Phone'.tr, 'phone', data),
_buildEditableRow('Email'.tr, 'email', data),
_buildEditableRow('Gender'.tr, 'gender', data),
_buildEditableRow('Birthdate'.tr, 'birthdate', data),
_buildEditableRow('National Number'.tr, 'national_number', data),
// _buildEditableRow('Religion'.tr, 'religion', data),
// _buildEditableRow('Occupation'.tr, 'occupation', data),
// _buildEditableRow('Education'.tr, 'education', data),
],
);
}
Widget _buildStatisticsSection(Map data) {
return CupertinoListSection.insetGrouped(
header: Text('Driver Statistics'.tr),
children: [
_buildInfoRow('Total Rides'.tr, data['countRide'].toString()),
_buildInfoRow('Average Rating'.tr, data['rating'].toString()),
_buildInfoRow('Total Payments'.tr, data['totalPayment'].toString()),
_buildInfoRow(
'Wallet Balance'.tr, data['totalDriverWallet'].toString()),
_buildInfoRow('Complaints'.tr, data['countComplaint'].toString()),
_buildInfoRow('Scam Reports'.tr, data['countScam'].toString()),
_buildInfoRow(
'Passengers Rated'.tr, data['DRatingPassengersCount'].toString()),
_buildInfoRow(
'Avg Passenger Rating'.tr, data['avgDRatingPassenger'].toString()),
],
);
}
// Read-only row widget
Widget _buildInfoRow(String label, String value) {
return CupertinoListTile(
title: Text(label),
trailing: Text(
value,
style: const TextStyle(color: CupertinoColors.systemGrey),
),
);
}
Widget _buildCarInfoSection(Map data) {
return CupertinoListSection.insetGrouped(
header: Text('Vehicle Information'.tr),
children: [
// _buildEditableRow('VIN'.tr, 'vin', data),
_buildEditableRow('Plate Number'.tr, 'car_plate', data),
_buildEditableRow('Make'.tr, 'make', data),
_buildEditableRow('Model'.tr, 'model', data),
_buildEditableRow('Year'.tr, 'year', data),
_buildEditableRow('Color'.tr, 'color', data),
_buildEditableRow('Fuel Type'.tr, 'fuel', data),
// _buildEditableRow('Displacement'.tr, 'displacement', data),
_buildEditableRow('Registration Date'.tr, 'registration_date', data),
_buildEditableRow('Expiration Date'.tr, 'expiration_date', data),
],
);
}
Widget _buildLicenseInfoSection(Map data) {
return CupertinoListSection.insetGrouped(
header: Text('License Information'.tr),
children: [
_buildEditableRow('License Type'.tr, 'license_type', data),
_buildEditableRow('Card ID'.tr, 'card_id', data),
_buildEditableRow('Issue Date'.tr, 'issue_date', data),
_buildEditableRow('Expiry Date'.tr, 'expiry_date', data),
_buildEditableRow('Categories'.tr, 'license_categories', data),
],
);
}
Widget _buildBankInfoSection(Map data) {
return CupertinoListSection.insetGrouped(
header: Text('Bank Information'.tr),
children: [
_buildEditableRow('Account Number'.tr, 'accountBank', data),
_buildEditableRow('Bank Code'.tr, 'bankCode', data),
],
);
}
Widget _buildCommunicationSection(Map data, BuildContext context) {
String phone = data['phone'] ?? '';
String name = data['first_name'] ?? '';
return CupertinoListSection.insetGrouped(
header: Text('Quick Communication'.tr),
children: [
CupertinoListTile(
title: Text('Call Driver'.tr),
leading: const Icon(CupertinoIcons.phone_fill, color: Colors.green),
onTap: () => mainController.makePhoneCall(phone),
),
CupertinoListTile(
title: Text('WhatsApp: Activation'.tr),
leading: const Icon(Icons.send, color: Colors.green),
onTap: () => mainController.launchCommunication(
'whatsapp',
phone,
'أهلاً بك يا كابتن $name في انطلق! تم تفعيل حسابك بنجاح وأصبحت مستعداً لاستقبال الرحلات.',
),
),
CupertinoListTile(
title: Text('WhatsApp: Missing Docs'.tr),
leading: const Icon(Icons.send, color: Colors.orange),
onTap: () => mainController.launchCommunication(
'whatsapp',
phone,
'مرحباً كابتن $name، يرجى تزويدنا بالأوراق الناقصة أو غير الواضحة عبر الواتساب لإكمال تفعيل حسابك.',
),
),
CupertinoListTile(
title: Text('WhatsApp: Support'.tr),
leading: const Icon(Icons.send, color: Colors.blue),
onTap: () => mainController.launchCommunication(
'whatsapp',
phone,
'مرحباً كابتن $name، معك الدعم الفني من شركة انطلق. كيف يمكنني مساعدتك اليوم؟',
),
),
],
);
}
}

View File

@@ -0,0 +1,223 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:siro_service/constant/colors.dart';
import 'package:siro_service/controller/mainController/main_controller.dart';
import 'package:siro_service/views/widgets/my_scafold.dart';
import 'package:siro_service/main.dart';
import 'package:siro_service/constant/box_name.dart';
import 'registration_captain_page.dart';
class DriversCantRegister extends StatelessWidget {
DriversCantRegister({super.key});
//
@override
Widget build(BuildContext context) {
Get.put(MainController());
// Unified action button (WhatsApp - Edit - etc)
Widget buildActionButton({
required IconData icon,
required Color color,
required VoidCallback onPressed,
}) {
return CupertinoButton(
padding: const EdgeInsets.symmetric(horizontal: 6.0),
onPressed: onPressed,
child: Icon(icon, color: color, size: 28),
);
}
return MyScaffold(
title: 'Drivers Want Register'.tr,
isleading: true,
body: [
GetBuilder<MainController>(builder: (mainController) {
return Column(
children: [
// Search
Padding(
padding: const EdgeInsets.fromLTRB(16, 8, 16, 8),
child: CupertinoSearchTextField(
keyboardType: TextInputType.phone,
onChanged: (value) => mainController.searchDrivers(value),
placeholder: 'Search by phone number'.tr,
),
),
// List
Expanded(
child: mainController.filteredDrivers.isEmpty
? Center(
child: Text(
'No drivers found'.tr,
style:
TextStyle(color: Colors.grey[600], fontSize: 16),
),
)
: ListView.builder(
itemCount: mainController.filteredDrivers.length,
itemBuilder: (context, index) {
final driver = mainController.filteredDrivers[index];
final notesController =
TextEditingController(text: driver['note'] ?? '');
return Card(
margin: const EdgeInsets.symmetric(
horizontal: 12.0, vertical: 8.0),
elevation: 3,
clipBehavior: Clip.antiAlias,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
side: BorderSide(
color: driver['note'] != null
? AppColor.secondaryColor
: Colors.transparent,
width: 2.5,
),
),
child: Padding(
padding:
const EdgeInsets.fromLTRB(16, 12, 16, 12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Header
Text(
'Driver Phone: ${driver['first_name'] ?? ''} ${driver['last_name'] ?? ''}',
style: const TextStyle(
fontWeight: FontWeight.bold,
fontSize: 16,
color: Colors.black87,
),
),
const Divider(height: 20),
// Phone Row
Row(
children: [
const Icon(
CupertinoIcons.phone_fill,
color: AppColor.primaryColor,
size: 22,
),
const SizedBox(width: 12),
Expanded(
child: InkWell(
onTap: () {
mainController.makePhoneCall(
driver['phone_number']);
},
child: Text(
driver['phone_number'] ?? 'N/A',
style: const TextStyle(
fontSize: 17,
letterSpacing: 1.2,
),
),
),
),
// WhatsApp button
buildActionButton(
icon: Icons.send,
color: const Color(0xFF25D366),
onPressed: () {
String message = "مرحباً،\n\n"
"يظهر لدينا في نظام تطبيق *انطلق* أنك لم تكمل عملية التسجيل بعد.\n"
"ندعوك لإكمال التسجيل للاستفادة من مزايا التطبيق والبدء بالعمل معنا.\n\n"
"إذا احتجت لأي مساعدة، تواصل معنا على خدمة العملاء:\n"
"+963 952 475 742\n\n"
"+963 952 475 740\n\n"
"فريق انطلق يتمنى لك يوماً سعيداً.";
mainController.launchCommunication(
'whatsapp',
driver['phone_number'],
message,
);
},
),
// Edit button → go to registration form
buildActionButton(
icon: CupertinoIcons
.pencil_ellipsis_rectangle,
color: AppColor.gold,
onPressed: () {
Get.to(
() => RegisterCaptain(),
arguments: {
"phone": driver['phone_number'],
"driverId": driver['driverId'],
},
);
},
),
buildActionButton(
icon: CupertinoIcons
.pencil_ellipsis_rectangle,
color: AppColor.redColor,
onPressed: () {
mainController
.deleteDriverNotCompleteRegistration(
driver['phone_number']);
},
),
],
),
const SizedBox(height: 16),
// Notes box
CupertinoTextField(
controller: notesController,
placeholder: "Additional comments".tr,
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: Colors.grey.shade100,
borderRadius: BorderRadius.circular(8),
),
maxLines: 2,
),
const SizedBox(height: 12),
// Save button
Align(
alignment: AlignmentDirectional.centerEnd,
child: CupertinoButton(
padding: const EdgeInsets.symmetric(
horizontal: 20),
color: AppColor.secondaryColor,
onPressed: () {
mainController
.saveNoteForDriverNotCompleteRegistration(
driver['phone_number'],
box.read(BoxName.employeename) ??
'none',
notesController.text,
);
},
child: Text('Save Notes'.tr),
),
),
],
),
),
);
},
),
),
],
);
}),
],
);
}
}

View File

@@ -0,0 +1,334 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:intl/intl.dart';
import 'package:siro_service/controller/mainController/main_controller.dart';
import 'package:siro_service/views/widgets/my_scafold.dart';
import '../../../constant/colors.dart';
import '../../../constant/links.dart';
import '../../../views/widgets/my_textField.dart';
import '../../functions/image.dart';
import '../../functions/launch.dart';
class EditCar extends StatelessWidget {
final Map carData;
const EditCar({super.key, required this.carData});
@override
Widget build(BuildContext context) {
Get.put(MainController());
return GetBuilder<MainController>(builder: (mainController) {
return MyScaffold(
title: 'Edit',
isleading: true,
action: Row(
children: [
IconButton(
onPressed: () {
makePhoneCall(carData['phone']);
},
icon: const Icon(Icons.phone),
),
IconButton(
onPressed: () {
launchCommunication('whatsapp', carData['phone'], '');
},
icon: const Icon(
Icons.message,
color: AppColor.greenColor,
),
),
],
),
body: [
ListView(
children: [
Column(
children: [
GestureDetector(
onLongPress: () async {
await ImageController().choosImage(AppLink.uploadEgypt,
carData['driverID'], 'car_front');
},
child: Image.network(
'https://sefer.click/sefer/card_image/car_front-${carData['driverID']}.jpg',
height: 200,
width: double.maxFinite,
fit: BoxFit.fill,
errorBuilder: (BuildContext context, Object exception,
StackTrace? stackTrace) {
// If the image fails to load, use the _copy version
return Image.network(
'https://sefer.click/sefer/card_image/car_front-${carData['driverID']}_copy.jpg',
height: 200,
width: double.maxFinite,
fit: BoxFit.fill,
);
},
),
),
GestureDetector(
onLongPress: () async {
await ImageController().choosImage(
AppLink.uploadEgypt, carData['id'], 'car_back');
},
child: Image.network(
'https://sefer.click/sefer/card_image/car_back-${carData['driverID']}.jpg',
height: 200,
width: double.maxFinite,
fit: BoxFit.fill,
errorBuilder: (BuildContext context, Object exception,
StackTrace? stackTrace) {
// If the image fails to load, use the _copy version
return Image.network(
'https://sefer.click/sefer/card_image/car_back-${carData['driverID']}_copy.jpg',
height: 200,
width: double.maxFinite,
fit: BoxFit.fill,
);
},
),
),
],
),
const SizedBox(height: 9),
Form(
key: mainController.formKey,
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
SizedBox(
width: Get.width * .6,
child: MyTextForm(
controller: mainController.carplateController,
label: 'car plate'.tr,
hint: 'car plate'.tr,
type: TextInputType.name,
),
),
IconButton(
onPressed: () async {
if (mainController.formKey.currentState!
.validate()) {
await mainController.editCarPlateNotEdit(
carData['driverID'].toString(),
mainController.carplateController.text,
mainController.colorController.text,
mainController.colorHex.value.toString(),
mainController.yearController.text,
mainController.makeController.text,
mainController.modelController.text,
mainController.expirationDateController.text,
mainController.ownerController.text,
);
}
},
icon: const Icon(
Icons.upload_outlined,
color: AppColor.blueColor,
),
),
],
),
// Other fields
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
SizedBox(
width: Get.width * .4,
child: MyTextForm(
controller: mainController.yearController,
label: 'Year'.tr,
hint: 'Year'.tr,
type: TextInputType.number,
),
),
SizedBox(
width: Get.width * .4,
child: DropdownButtonFormField<String>(
decoration: InputDecoration(
labelText: 'Color'.tr, // Localized label
),
value: mainController.colorHex.value.isEmpty
? null
: mainController.colorHex
.value, // Use the hex value as the current value
items: [
{'red'.tr: '#FF0000'},
{'green'.tr: '#008000'},
{'blue'.tr: '#0000FF'},
{'black'.tr: '#000000'},
{'white'.tr: '#FFFFFF'},
{'yellow'.tr: '#FFFF00'},
{'purple'.tr: '#800080'},
{'orange'.tr: '#FFA500'},
{'pink'.tr: '#FFC0CB'},
{'brown'.tr: '#A52A2A'},
{'gray'.tr: '#808080'},
{'cyan'.tr: '#00FFFF'},
{'magenta'.tr: '#FF00FF'},
{'lime'.tr: '#00FF00'},
{'indigo'.tr: '#4B0082'},
{'violet'.tr: '#EE82EE'},
{'gold'.tr: '#FFD700'},
{'silver'.tr: '#C0C0C0'},
{'teal'.tr: '#008080'},
{'navy'.tr: '#000080'},
].map((colorMap) {
String colorName = colorMap.keys.first;
String colorValue = colorMap.values.first;
return DropdownMenuItem<String>(
value: colorValue,
child: Text(colorName),
);
}).toList(),
onChanged: (value) {
if (value != null) {
// Find the selected color name based on the hex value
String selectedColorName = '';
for (var colorMap in [
{'red'.tr: '#FF0000'},
{'green'.tr: '#008000'},
{'blue'.tr: '#0000FF'},
{'black'.tr: '#000000'},
{'white'.tr: '#FFFFFF'},
{'yellow'.tr: '#FFFF00'},
{'purple'.tr: '#800080'},
{'orange'.tr: '#FFA500'},
{'pink'.tr: '#FFC0CB'},
{'brown'.tr: '#A52A2A'},
{'gray'.tr: '#808080'},
{'cyan'.tr: '#00FFFF'},
{'magenta'.tr: '#FF00FF'},
{'lime'.tr: '#00FF00'},
{'indigo'.tr: '#4B0082'},
{'violet'.tr: '#EE82EE'},
{'gold'.tr: '#FFD700'},
{'silver'.tr: '#C0C0C0'},
{'teal'.tr: '#008080'},
{'navy'.tr: '#000080'},
]) {
if (colorMap.values.first == value) {
selectedColorName = colorMap.keys.first;
break;
}
}
mainController.colorController.text =
selectedColorName;
mainController.colorHex.value = value;
}
},
),
),
],
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
SizedBox(
width: Get.width * .4,
child: MyTextForm(
controller: mainController.makeController,
label: 'Make'.tr,
hint: 'Make'.tr,
type: TextInputType.name,
),
),
SizedBox(
width: Get.width * .4,
child: MyTextForm(
controller: mainController.modelController,
label: 'Model'.tr,
hint: 'Model'.tr,
type: TextInputType.name,
),
),
],
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
SizedBox(
width: Get.width * .4,
child: TextField(
controller:
mainController.expirationDateController,
decoration: InputDecoration(
labelText: 'Expiration Date'.tr,
hintText: 'Expiration Date'.tr,
),
readOnly:
true, // Make the field read-only to prevent manual input
onTap: () async {
DateTime pickedDate =
DateTime.now(); // Declare the variable here
await showCupertinoModalPopup<void>(
context: context,
builder: (context) => Container(
height: 250,
color: Colors.white,
child: Column(
children: [
SizedBox(
height: 150,
child: CupertinoDatePicker(
initialDateTime: pickedDate,
minimumDate: DateTime(
1955), // Set the starting date
maximumDate: DateTime(
2034), // Set the ending date
mode: CupertinoDatePickerMode.date,
onDateTimeChanged:
(DateTime dateTime) {
pickedDate = dateTime;
},
),
),
CupertinoButton(
child: Text('Done'.tr),
onPressed: () {
String formattedDate =
DateFormat('yyyy-MM-dd')
.format(pickedDate);
mainController
.expirationDateController
.text =
formattedDate.toString();
Navigator.of(context).pop();
},
),
],
),
),
);
},
),
),
SizedBox(
width: Get.width * .4,
child: MyTextForm(
controller: mainController.ownerController,
label: 'Owner'.tr,
hint: 'Owner'.tr,
type: TextInputType.name,
),
),
],
),
],
),
)
],
)
]);
});
}
}

View File

@@ -0,0 +1,48 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:siro_service/constant/style.dart';
import 'package:siro_service/controller/mainController/pages/edit_car.dart';
import 'package:siro_service/views/widgets/my_scafold.dart';
import '../../functions/encrypt_decrypt.dart';
import '../main_controller.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
class EditCarPlate extends StatelessWidget {
const EditCarPlate({super.key});
@override
Widget build(BuildContext context) {
Get.put(MainController());
return GetBuilder<MainController>(builder: (mainController) {
return MyScaffold(
title: 'Edit car details'.tr,
isleading: true,
body: [
Expanded(
child: ListView.builder(
padding: const EdgeInsets.all(16.0),
itemCount: mainController
.carPlateNotEdit.length, // 10 fields + 1 save button
itemBuilder: (context, index) {
var carData = mainController.carPlateNotEdit[index];
return Padding(
padding: const EdgeInsets.all(8.0),
child: InkWell(
onTap: () {
Get.to(EditCar(carData: carData));
},
child: Container(
decoration: AppStyle.boxDecoration1,
child: Text((carData['owner']))),
),
);
}),
),
],
);
});
}
}

View File

@@ -0,0 +1,56 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:siro_service/views/widgets/my_scafold.dart';
import '../../best_driver_controllers.dart';
import '../../functions/encrypt_decrypt.dart';
class DriverTheBestGiza extends StatelessWidget {
const DriverTheBestGiza({super.key});
@override
Widget build(BuildContext context) {
Get.put(DriverTheBestGizaController(), permanent: true);
return MyScaffold(
title: 'Giza'.tr,
body: [
GetBuilder<DriverTheBestGizaController>(builder: (driverthebest) {
return driverthebest.driver.isNotEmpty
? ListView.builder(
itemCount: driverthebest.driver.length,
itemBuilder: (context, index) {
final driver = driverthebest.driver[index];
return ListTile(
leading: CircleAvatar(
child: Text(
(int.parse(driver['driver_count'] * 5) / 3600)
.toStringAsFixed(0),
),
),
title: Text((driver['name_arabic']) ??
'Unknown Name'),
subtitle: Text(
'Phone: ${(driver['phone']) ?? 'N/A'}'),
trailing: IconButton(
onPressed: () async {
Get.defaultDialog(
title:
'are you sure to pay to this driver gift'.tr,
middleText: '',
onConfirm: () async {},
onCancel: () => Get.back());
},
icon: const Icon(Icons.wallet_giftcard_rounded),
),
);
},
)
: const Center(
child: Text('No drivers available.'),
);
})
],
isleading: true,
);
}
}

View File

@@ -0,0 +1,485 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:siro_service/constant/colors.dart';
// Importa tu nuevo controlador de servicio
import 'package:siro_service/controller/mainController/ragister_service_controller.dart';
import 'package:siro_service/views/widgets/elevated_btn.dart';
import 'package:siro_service/views/widgets/my_scafold.dart';
// El import del antiguo controlador ya no es necesario
// import '../registration_captain_controller.dart';
class RegisterCaptain extends StatelessWidget {
const RegisterCaptain({super.key});
@override
Widget build(BuildContext context) {
// Instancia el NUEVO controlador
final controller = Get.put(RegisterCaptainServiceController());
return MyScaffold(
title: 'Syrian Documents Check'.tr,
isleading: true,
body: [
Obx(() {
if (controller.isLoading.value) {
return const Center(child: CircularProgressIndicator());
}
return Column(
children: [
Padding(
padding: const EdgeInsets.symmetric(vertical: 16.0),
child: StepIndicator(
currentStep: controller.currentPageIndex.value,
totalSteps: 4,
),
),
Expanded(
child: PageView(
controller: controller.pageController,
onPageChanged: (index) {
controller.currentPageIndex.value = index;
},
children: [
// <-- PÁGINAS REACTIVADAS CON EL NUEVO CONTROLADOR -->
_buildSyrianDriverLicenseFront(context, controller),
_buildSyrianDriverLicenseBack(context, controller),
_buildSyrianCarLicenseFront(context, controller),
_buildSyrianCarLicenseBack(controller),
],
),
),
_buildNavigationControls(controller),
],
);
}),
],
);
}
// Este método ya estaba usando el controlador correcto
Widget _buildNavigationControls(RegisterCaptainServiceController controller) {
bool isLastPage = controller.currentPageIndex.value == 3;
bool isFirstPage = controller.currentPageIndex.value == 0;
return Padding(
padding: const EdgeInsets.all(16.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
if (!isFirstPage)
MyElevatedButton(
title: 'Back'.tr,
onPressed: controller.previousPage,
kolor: Colors.grey,
),
if (isFirstPage) const Spacer(),
MyElevatedButton(
title: isLastPage ? 'Save and Activate'.tr : 'Next'.tr,
onPressed: isLastPage
? controller.updateAndActivateSyrianDriver
: controller.nextPage,
kolor: isLastPage ? AppColor.greenColor : AppColor.primaryColor,
),
],
),
);
}
Widget _buildTextField({
required String label,
required TextEditingController controller,
IconData? icon,
TextInputType keyboardType = TextInputType.text,
VoidCallback? onTap,
String? hintText,
}) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 8.0),
child: TextFormField(
controller: controller,
keyboardType: keyboardType,
readOnly: onTap != null,
onTap: onTap,
decoration: InputDecoration(
labelText: label.tr,
hintText: hintText,
prefixIcon:
icon != null ? Icon(icon, color: AppColor.secondaryColor) : null,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
borderSide:
const BorderSide(color: AppColor.secondaryColor, width: 2),
),
),
),
);
}
Widget _buildPageContent({
required RxString imageUrl,
required List<Widget> formFields,
}) {
return SingleChildScrollView(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
// ClipRRect(
// borderRadius: BorderRadius.circular(12),
// child: Image.network(
// imageUrl.value,
// fit: BoxFit.fitWidth,
// errorBuilder: (context, error, stackTrace) {
// return Container(
// height: 200,
// color: Colors.grey[200],
// child: Center(
// child: Text(
// 'Image not available'.tr,
// style: const TextStyle(color: Colors.grey),
// ),
// ),
// );
// },
// ),
// ),
// const SizedBox(height: 20),
const Divider(),
...formFields,
],
),
);
}
// <-- MODIFICADO: Usa RegisterCaptainServiceController -->
Widget _buildColorDropdown(RegisterCaptainServiceController controller) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 8.0),
child: Obx(
() => DropdownButtonFormField<String>(
value: controller.colorHex.value.isEmpty
? null
: controller.colorHex.value,
isExpanded: true,
decoration: InputDecoration(
labelText: 'Car Color'.tr,
prefixIcon: Icon(Icons.color_lens, color: AppColor.secondaryColor),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
),
),
// <-- MODIFICADO: Usa la referencia estática del nuevo controlador -->
items: RegisterCaptainServiceController.kCarColorOptions.map((opt) {
final hex = opt['hex']!;
final key = opt['key']!;
return DropdownMenuItem<String>(
value: hex,
child: Row(
children: [
Container(
width: 18,
height: 18,
decoration: BoxDecoration(
color: controller.hexToColor(hex),
shape: BoxShape.circle,
border: Border.all(color: Colors.black12),
),
),
const SizedBox(width: 12),
Expanded(child: Text(key.tr)),
],
),
);
}).toList(),
onChanged: (hex) {
if (hex != null) {
controller.updateColorSelection(hex);
}
},
),
),
);
}
// <-- MODIFICADO: Usa RegisterCaptainServiceController -->
Widget _buildGenderDropdown(RegisterCaptainServiceController controller) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 8.0),
child: Obx(
() => DropdownButtonFormField<String>(
value: controller.selectedGender.value.isEmpty
? null
: controller.selectedGender.value,
isExpanded: true,
decoration: InputDecoration(
labelText: 'Gender'.tr,
prefixIcon: Icon(Icons.wc, color: AppColor.secondaryColor),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
),
),
items: ['Male', 'Female'].map((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value.tr),
);
}).toList(),
onChanged: (String? newValue) {
if (newValue != null) {
controller.selectedGender.value = newValue;
}
},
),
),
);
}
// <-- MODIFICADO: Usa RegisterCaptainServiceController -->
Widget _buildFuelDropdown(RegisterCaptainServiceController controller) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 8.0),
child: Obx(
() => DropdownButtonFormField<String>(
// <-- MODIFICADO: Usa la referencia estática del nuevo controlador -->
value: RegisterCaptainServiceController.kFuelOptions
.contains(controller.selectedFuel.value)
? controller.selectedFuel.value
: null,
isExpanded: true,
decoration: InputDecoration(
labelText: 'Fuel Type'.tr,
prefixIcon:
Icon(Icons.local_gas_station, color: AppColor.secondaryColor),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
),
),
// <-- MODIFICADO: Usa la referencia estática del nuevo controlador -->
items:
RegisterCaptainServiceController.kFuelOptions.map((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value.tr),
);
}).toList(),
onChanged: (String? newValue) {
if (newValue != null) {
controller.selectedFuel.value = newValue;
}
},
),
),
);
}
// --- PAGE 1: Driver License Front ---
// <-- MODIFICADO: Usa RegisterCaptainServiceController -->
Widget _buildSyrianDriverLicenseFront(
BuildContext context, RegisterCaptainServiceController controller) {
return _buildPageContent(
imageUrl: controller.docUrls['driver_license_front']!,
formFields: [
Row(
children: [
Expanded(
child: _buildTextField(
label: 'First Name',
controller: controller.firstNameController,
keyboardType: TextInputType.name),
),
const SizedBox(width: 8),
Expanded(
child: _buildTextField(
label: 'Last Name',
controller: controller.lastNameController,
keyboardType: TextInputType.name),
),
],
),
_buildTextField(
label: 'Phone Number',
controller: controller.phoneController,
hintText: 'e.g., 963992952235',
icon: Icons.phone,
keyboardType: TextInputType.phone,
),
_buildTextField(
label: 'Place of Registration',
controller: controller.siteController,
icon: Icons.location_city),
Row(
children: [
Expanded(
child: _buildTextField(
label: 'National Number',
controller: controller.nationalNumberController,
keyboardType: TextInputType.number,
icon: Icons.fingerprint),
),
const SizedBox(width: 8),
Expanded(child: _buildGenderDropdown(controller)),
],
),
_buildTextField(
label: 'Birthdate',
controller: controller.birthdateController,
icon: Icons.cake,
onTap: () =>
controller.selectDate(context, controller.birthdateController)),
],
);
}
// --- PAGE 2: Driver License Back ---
// <-- MODIFICADO: Usa RegisterCaptainServiceController -->
Widget _buildSyrianDriverLicenseBack(
BuildContext context, RegisterCaptainServiceController controller) {
return _buildPageContent(
imageUrl: controller.docUrls['driver_license_back']!,
formFields: [
_buildTextField(
label: 'License Category',
controller: controller.licenseCategoriesController),
_buildTextField(
label: 'Expiry Date',
controller: controller.expiryDateController,
icon: Icons.event_busy,
onTap: () => controller.selectDate(
context, controller.expiryDateController)),
],
);
}
// --- PAGE 3: Car License Front ---
// <-- MODIFICADO: Usa RegisterCaptainServiceController -->
Widget _buildSyrianCarLicenseFront(
BuildContext context, RegisterCaptainServiceController controller) {
return _buildPageContent(
imageUrl: controller.docUrls['car_license_front']!,
formFields: [
_buildTextField(
label: 'Owner Name',
controller: controller.ownerController,
keyboardType: TextInputType.name,
icon: Icons.person_search),
Row(
children: [
Expanded(child: _buildColorDropdown(controller)),
const SizedBox(width: 8),
Expanded(
child: _buildTextField(
label: 'Car Plate',
controller: controller.carPlateController,
),
),
],
),
// _buildTextField(
// label: 'VIN',
// controller: controller.vinController,
// icon: Icons.confirmation_number),
Row(
children: [
Expanded(
child: _buildTextField(
label: 'License Issue Date',
controller: controller.licenseIssueDateController,
onTap: () => controller.selectDate(
context, controller.licenseIssueDateController))),
const SizedBox(width: 8),
Expanded(
child: _buildTextField(
label: 'License Expiry Date',
controller: controller.carLicenseExpiryDateController,
onTap: () => controller.selectDate(
context, controller.carLicenseExpiryDateController))),
],
),
],
);
}
// --- PAGE 4: Car License Back ---
// <-- MODIFICADO: Usa RegisterCaptainServiceController -->
Widget _buildSyrianCarLicenseBack(
RegisterCaptainServiceController controller) {
return _buildPageContent(
imageUrl: controller.docUrls['car_license_back']!,
formFields: [
Row(
children: [
Expanded(
child: _buildTextField(
label: 'Make',
controller: controller.makeController,
)),
const SizedBox(width: 8),
Expanded(
child: _buildTextField(
label: 'Model',
controller: controller.modelController,
)),
],
),
Row(
children: [
Expanded(
child: _buildTextField(
label: 'Year',
controller: controller.yearController,
keyboardType: TextInputType.number)),
const SizedBox(width: 8),
Expanded(child: _buildFuelDropdown(controller)),
],
),
],
);
}
}
class StepIndicator extends StatelessWidget {
final int currentStep;
final int totalSteps;
const StepIndicator({
super.key,
required this.currentStep,
required this.totalSteps,
});
@override
Widget build(BuildContext context) {
return Column(
children: [
Text(
'${'Step'.tr} ${currentStep + 1} ${'of'.tr} $totalSteps',
style: const TextStyle(
fontSize: 18, fontWeight: FontWeight.bold, color: Colors.black54),
),
const SizedBox(height: 8),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: List.generate(totalSteps, (index) {
return Container(
margin: const EdgeInsets.symmetric(horizontal: 4),
width: 30,
height: 8,
decoration: BoxDecoration(
color: index <= currentStep
? AppColor.primaryColor
: Colors.grey.shade300,
borderRadius: BorderRadius.circular(4),
),
);
}),
),
],
);
}
}

View File

@@ -0,0 +1,79 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:siro_service/controller/mainController/main_controller.dart';
import 'package:siro_service/views/widgets/my_scafold.dart';
import '../../functions/encrypt_decrypt.dart';
class PassengersCantRegister extends StatelessWidget {
PassengersCantRegister({super.key});
@override
Widget build(BuildContext context) {
Get.put(MainController());
return MyScaffold(
title: 'Passengers Cant Register'.tr,
isleading: true,
body: [
GetBuilder<MainController>(builder: (mainController) {
return ListView.builder(
itemCount: mainController.passengerNotCompleteRegistration.length,
itemBuilder: (context, index) {
final passenger =
mainController.passengerNotCompleteRegistration[index];
return Padding(
padding: const EdgeInsets.all(8.0),
child: CupertinoFormSection(
header: Text('Passenger ID: ${passenger['id']}'),
children: [
InkWell(
onTap: () =>
mainController.makePhoneCall(passenger['phone']),
child: CupertinoFormRow(
prefix: Text(' phone'.tr),
child: CupertinoTextFormFieldRow(
initialValue: ((passenger['phone'])),
readOnly: true,
placeholder: 'Phone Number'.tr,
),
),
),
CupertinoFormRow(
prefix: Text('Created At'.tr),
child: CupertinoTextFormFieldRow(
initialValue: passenger['created_at'],
readOnly: true,
placeholder: 'Created At',
),
),
CupertinoFormRow(
prefix: Text('Notes'.tr),
child: CupertinoTextFormFieldRow(
controller: mainController.notesController,
placeholder:
passenger['note'] ?? 'Enter notes after call'.tr,
),
),
CupertinoButton(
child: Text('Save Notes'.tr),
onPressed: () {
// Save the notes for the Passenger
String notes = mainController.notesController.text;
mainController
.saveNoteForPassengerNotCompleteRegistration(
passenger['phone_number'], 'girls name', notes);
print('Notes for Passenger ${passenger['id']}: $notes');
},
),
],
),
);
},
);
}),
],
);
}
}

View File

@@ -0,0 +1,146 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:siro_service/controller/mainController/main_controller.dart';
import 'package:siro_service/views/widgets/my_scafold.dart';
class PassengersPage extends StatelessWidget {
PassengersPage({super.key});
final MainController mainController = MainController();
@override
Widget build(BuildContext context) {
return GetBuilder<MainController>(
builder: (mainController) {
final data = _extractPassengerData(mainController);
return MyScaffold(
title: data != null
? '${data['first_name'] ?? ''} ${data['last_name'] ?? ''}'
: 'Passenger Not Found'.tr,
isleading: true,
body: [
if (data != null)
ListView(
children: [
_buildPersonalInfoCard(data),
_buildLatestRideCard(data),
_buildWalletInfoCard(data),
],
)
else
const Center(
child: Padding(
padding: EdgeInsets.all(24.0),
child: Text(
'No passenger data available.',
style: TextStyle(fontSize: 18, fontWeight: FontWeight.w500),
),
),
),
],
);
},
);
}
/// Helper method to extract the passenger data safely.
Map? _extractPassengerData(MainController controller) {
final passengerData = controller.passengerData;
if (passengerData == null ||
passengerData['message'] == null ||
passengerData['message'].isEmpty) {
return null;
}
return passengerData['message'][0];
}
Widget _buildPersonalInfoCard(Map data) {
return _buildCard(
title: 'Personal Information'.tr,
children: [
_buildInfoRow('Phone'.tr, data['phone']),
_buildInfoRow('Email'.tr, data['email']),
_buildInfoRow('Gender'.tr, data['gender']),
_buildInfoRow('Birthdate'.tr, data['birthdate']),
// _buildInfoRow('Education'.tr, data['education']),
_buildInfoRow('Employment'.tr, data['employmentType']),
_buildInfoRow('Marital Status'.tr, data['maritalStatus']),
],
);
}
Widget _buildLatestRideCard(Map data) {
return _buildCard(
title: 'Latest Ride'.tr,
children: [
_buildInfoRow('Ride ID'.tr, data['ride_id']),
_buildInfoRow('Date'.tr, data['ride_date']),
_buildInfoRow('Start Time'.tr, data['ride_time']),
_buildInfoRow('End Time'.tr, data['ride_endtime']),
_buildInfoRow(
'Price'.tr, data['price'] != null ? '\$${data['price']}' : null),
_buildInfoRow('Status'.tr, data['ride_status']),
_buildInfoRow('Payment Method'.tr, data['ride_payment_method']),
_buildInfoRow('Car Type'.tr, data['car_type']),
_buildInfoRow(
'Distance'.tr,
data['distance'] != null
? '${(data['distance'] as num).toStringAsFixed(2)} km'
: null),
],
);
}
Widget _buildWalletInfoCard(Map data) {
return _buildCard(
title: 'Wallet Information'.tr,
children: [
_buildInfoRow(
'Wallet Balance'.tr,
data['passenger_wallet_balance'] != null
? '\$${data['passenger_wallet_balance']}'
: null),
_buildInfoRow(
'Last Payment Amount'.tr,
data['passenger_payment_amount'] != null
? '\$${data['passenger_payment_amount']}'
: null),
_buildInfoRow(
'Last Payment Method'.tr, data['passenger_payment_method']),
],
);
}
Widget _buildCard({required String title, required List<Widget> children}) {
return Card(
margin: const EdgeInsets.all(16),
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(title,
style:
const TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
const SizedBox(height: 16),
...children,
],
),
),
);
}
Widget _buildInfoRow(String label, dynamic value) {
final displayValue = value?.toString() ?? 'N/A';
return Padding(
padding: const EdgeInsets.symmetric(vertical: 4),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(label, style: const TextStyle(fontWeight: FontWeight.bold)),
Flexible(child: Text(displayValue, overflow: TextOverflow.ellipsis)),
],
),
);
}
}

View File

@@ -0,0 +1,458 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:siro_service/constant/colors.dart';
import 'package:siro_service/views/widgets/elevated_btn.dart';
import 'package:siro_service/views/widgets/my_scafold.dart';
import '../registration_captain_controller.dart';
class RegisterCaptain extends StatelessWidget {
const RegisterCaptain({super.key});
@override
Widget build(BuildContext context) {
final controller = Get.put(RegisterCaptainController());
return MyScaffold(
title: 'Syrian Documents Check'.tr,
isleading: true,
body: [
Obx(() {
if (controller.isLoading.value) {
return const Center(child: CircularProgressIndicator());
}
return Column(
children: [
Padding(
padding: const EdgeInsets.symmetric(vertical: 16.0),
child: StepIndicator(
currentStep: controller.currentPageIndex.value,
totalSteps: 4,
),
),
Expanded(
child: PageView(
controller: controller.pageController,
onPageChanged: (index) {
controller.currentPageIndex.value = index;
},
children: [
_buildSyrianDriverLicenseFront(context, controller),
_buildSyrianDriverLicenseBack(context, controller),
_buildSyrianCarLicenseFront(context, controller),
_buildSyrianCarLicenseBack(controller),
],
),
),
_buildNavigationControls(controller),
],
);
}),
],
);
}
Widget _buildNavigationControls(RegisterCaptainController controller) {
bool isLastPage = controller.currentPageIndex.value == 3;
bool isFirstPage = controller.currentPageIndex.value == 0;
return Padding(
padding: const EdgeInsets.all(16.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
if (!isFirstPage)
MyElevatedButton(
title: 'Back'.tr,
onPressed: controller.previousPage,
kolor: Colors.grey,
),
if (isFirstPage) const Spacer(),
MyElevatedButton(
title: isLastPage ? 'Save and Activate'.tr : 'Next'.tr,
onPressed: isLastPage
? controller.updateAndActivateSyrianDriver
: controller.nextPage,
kolor: isLastPage ? AppColor.greenColor : AppColor.primaryColor,
),
],
),
);
}
Widget _buildTextField({
required String label,
required TextEditingController controller,
IconData? icon,
TextInputType keyboardType = TextInputType.text,
VoidCallback? onTap,
}) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 8.0),
child: TextFormField(
controller: controller,
keyboardType: keyboardType,
readOnly: onTap != null,
onTap: onTap,
decoration: InputDecoration(
labelText: label.tr,
prefixIcon:
icon != null ? Icon(icon, color: AppColor.secondaryColor) : null,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
borderSide:
const BorderSide(color: AppColor.secondaryColor, width: 2),
),
),
),
);
}
Widget _buildPageContent({
required RxString imageUrl,
required List<Widget> formFields,
}) {
return SingleChildScrollView(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
ClipRRect(
borderRadius: BorderRadius.circular(12),
child: Image.network(
imageUrl.value,
fit: BoxFit.fitWidth,
errorBuilder: (context, error, stackTrace) {
return Container(
height: 200,
color: Colors.grey[200],
child: Center(
child: Text(
'Image not available'.tr,
style: const TextStyle(color: Colors.grey),
),
),
);
},
),
),
const SizedBox(height: 20),
const Divider(),
...formFields,
],
),
);
}
Widget _buildColorDropdown(RegisterCaptainController controller) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 8.0),
child: Obx(
() => DropdownButtonFormField<String>(
value: controller.colorHex.value.isEmpty
? null
: controller.colorHex.value,
isExpanded: true,
decoration: InputDecoration(
labelText: 'Car Color'.tr,
prefixIcon: Icon(Icons.color_lens, color: AppColor.secondaryColor),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
),
),
items: RegisterCaptainController.kCarColorOptions.map((opt) {
final hex = opt['hex']!;
final key = opt['key']!;
return DropdownMenuItem<String>(
value: hex,
child: Row(
children: [
Container(
width: 18,
height: 18,
decoration: BoxDecoration(
color: controller.hexToColor(hex),
shape: BoxShape.circle,
border: Border.all(color: Colors.black12),
),
),
const SizedBox(width: 12),
Expanded(child: Text(key.tr)),
],
),
);
}).toList(),
onChanged: (hex) {
if (hex != null) {
controller.updateColorSelection(hex);
}
},
),
),
);
}
Widget _buildGenderDropdown(RegisterCaptainController controller) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 8.0),
child: Obx(
() => DropdownButtonFormField<String>(
value: controller.selectedGender.value.isEmpty
? null
: controller.selectedGender.value,
isExpanded: true,
decoration: InputDecoration(
labelText: 'Gender'.tr,
prefixIcon: Icon(Icons.wc, color: AppColor.secondaryColor),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
),
),
items: ['Male', 'Female'].map((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value.tr),
);
}).toList(),
onChanged: (String? newValue) {
if (newValue != null) {
controller.selectedGender.value = newValue;
}
},
),
),
);
}
Widget _buildFuelDropdown(RegisterCaptainController controller) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 8.0),
child: Obx(
() => DropdownButtonFormField<String>(
value: RegisterCaptainController.kFuelOptions
.contains(controller.selectedFuel.value)
? controller.selectedFuel.value
: null,
isExpanded: true,
decoration: InputDecoration(
labelText: 'Fuel Type'.tr,
prefixIcon:
Icon(Icons.local_gas_station, color: AppColor.secondaryColor),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
),
),
items: RegisterCaptainController.kFuelOptions.map((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value.tr),
);
}).toList(),
onChanged: (String? newValue) {
if (newValue != null) {
controller.selectedFuel.value = newValue;
}
},
),
),
);
}
// --- PAGE 1: Driver License Front ---
Widget _buildSyrianDriverLicenseFront(
BuildContext context, RegisterCaptainController controller) {
return _buildPageContent(
imageUrl: controller.docUrls['driver_license_front']!,
formFields: [
Row(
children: [
Expanded(
child: _buildTextField(
label: 'First Name',
controller: controller.firstNameController,
keyboardType: TextInputType.name),
),
const SizedBox(width: 8),
Expanded(
child: _buildTextField(
label: 'Last Name',
controller: controller.lastNameController,
keyboardType: TextInputType.name),
),
],
),
_buildTextField(
label: 'Place of Registration',
controller: controller.siteController,
icon: Icons.location_city),
Row(
children: [
Expanded(
child: _buildTextField(
label: 'National Number',
controller: controller.nationalNumberController,
keyboardType: TextInputType.number,
icon: Icons.fingerprint),
),
const SizedBox(width: 8),
Expanded(child: _buildGenderDropdown(controller)),
],
),
// _buildTextField(
// label: 'Birthdate',
// controller: controller.birthdateController,
// icon: Icons.cake,
// onTap: () =>
// controller.selectDate(context, controller.birthdateController)),
],
);
}
// --- PAGE 2: Driver License Back ---
Widget _buildSyrianDriverLicenseBack(
BuildContext context, RegisterCaptainController controller) {
return _buildPageContent(
imageUrl: controller.docUrls['driver_license_back']!,
formFields: [
_buildTextField(
label: 'License Category',
controller: controller.licenseCategoriesController),
_buildTextField(
label: 'Expiry Date',
controller: controller.expiryDateController,
icon: Icons.event_busy,
onTap: () => controller.selectDate(
context, controller.expiryDateController)),
],
);
}
// --- PAGE 3: Car License Front ---
Widget _buildSyrianCarLicenseFront(
BuildContext context, RegisterCaptainController controller) {
return _buildPageContent(
imageUrl: controller.docUrls['car_license_front']!,
formFields: [
_buildTextField(
label: 'Owner Name',
controller: controller.ownerController,
keyboardType: TextInputType.name,
icon: Icons.person_search),
Row(
children: [
Expanded(child: _buildColorDropdown(controller)),
const SizedBox(width: 8),
Expanded(
child: _buildTextField(
label: 'Car Plate',
controller: controller.carPlateController,
),
),
],
),
// _buildTextField(
// label: 'VIN',
// controller: controller.vinController,
// icon: Icons.confirmation_number),
Row(
children: [
Expanded(
child: _buildTextField(
label: 'License Issue Date',
controller: controller.licenseIssueDateController,
onTap: () => controller.selectDate(
context, controller.licenseIssueDateController))),
const SizedBox(width: 8),
Expanded(
child: _buildTextField(
label: 'License Expiry Date',
controller: controller.carLicenseExpiryDateController,
onTap: () => controller.selectDate(
context, controller.carLicenseExpiryDateController))),
],
),
],
);
}
// --- PAGE 4: Car License Back ---
Widget _buildSyrianCarLicenseBack(RegisterCaptainController controller) {
return _buildPageContent(
imageUrl: controller.docUrls['car_license_back']!,
formFields: [
Row(
children: [
Expanded(
child: _buildTextField(
label: 'Make',
controller: controller.makeController,
)),
const SizedBox(width: 8),
Expanded(
child: _buildTextField(
label: 'Model',
controller: controller.modelController,
)),
],
),
Row(
children: [
Expanded(
child: _buildTextField(
label: 'Year',
controller: controller.yearController,
keyboardType: TextInputType.number)),
const SizedBox(width: 8),
Expanded(child: _buildFuelDropdown(controller)),
],
),
],
);
}
}
class StepIndicator extends StatelessWidget {
final int currentStep;
final int totalSteps;
const StepIndicator({
super.key,
required this.currentStep,
required this.totalSteps,
});
@override
Widget build(BuildContext context) {
return Column(
children: [
Text(
'${'Step'.tr} ${currentStep + 1} ${'of'.tr} $totalSteps',
style: const TextStyle(
fontSize: 18, fontWeight: FontWeight.bold, color: Colors.black54),
),
const SizedBox(height: 8),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: List.generate(totalSteps, (index) {
return Container(
margin: const EdgeInsets.symmetric(horizontal: 4),
width: 30,
height: 8,
decoration: BoxDecoration(
color: index <= currentStep
? AppColor.primaryColor
: Colors.grey.shade300,
borderRadius: BorderRadius.circular(4),
),
);
}),
),
],
);
}
}

View File

@@ -0,0 +1,223 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:siro_service/constant/colors.dart';
import 'package:siro_service/constant/style.dart';
import 'package:siro_service/views/widgets/elevated_btn.dart';
import 'package:siro_service/views/widgets/my_scafold.dart';
import '../main_controller.dart';
class WelcomeCall extends StatelessWidget {
const WelcomeCall({super.key});
@override
Widget build(BuildContext context) {
final controller = Get.put(MainController());
return MyScaffold(
title: 'Welcome Drivers'.tr,
isleading: true,
body: [
GetBuilder<MainController>(builder: (mainController) {
final drivers = mainController.newDriverRegister;
if (drivers.isEmpty) {
return const Padding(
padding: EdgeInsets.all(32.0),
child: Center(
child: Text(
'No new drivers found.',
style: TextStyle(fontSize: 18),
),
),
);
}
return CupertinoScrollbar(
child: ListView.builder(
padding: const EdgeInsets.only(bottom: 20),
itemCount: drivers.length,
itemBuilder: (context, index) {
final driver = drivers[index];
return DriverCard(driver: driver);
},
),
);
}),
],
);
}
}
class DriverCard extends StatelessWidget {
final Map<String, dynamic> driver;
const DriverCard({super.key, required this.driver});
Widget buildActionButton({
required IconData icon,
required Color color,
required VoidCallback onPressed,
}) {
return CupertinoButton(
padding: const EdgeInsets.symmetric(horizontal: 6.0),
onPressed: onPressed,
child: Icon(icon, color: color, size: 28),
);
}
@override
Widget build(BuildContext context) {
final controller = Get.find<MainController>();
return CupertinoCard(
margin: const EdgeInsets.all(16.0),
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// حالة التلوين حسب isCall
Container(
decoration: AppStyle.boxDecoration1.copyWith(
color: driver['isCall'].toString() == '1'
? AppColor.greenColor
: AppColor.accentColor,
),
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4),
child: Text(
'Driver Information'.tr,
style: CupertinoTheme.of(context).textTheme.navTitleTextStyle,
),
),
const SizedBox(height: 12),
InfoText(
'Name'.tr, driver['first_name'] + ' ' + driver['last_name']),
InfoText('Phone'.tr, driver['phone']),
InfoText('Email'.tr, driver['email']),
InfoText('License Type'.tr, driver['license_type']),
InfoText('License Categories'.tr, driver['license_categories']),
InfoText('National Number'.tr, driver['national_number']),
InfoText('Occupation'.tr, driver['occupation']),
const SizedBox(height: 12),
Text('Notes:'.tr,
style: CupertinoTheme.of(context).textTheme.navTitleTextStyle),
const SizedBox(height: 8),
CupertinoTextField(
controller: controller.notesController,
placeholder: driver['notes'] ?? 'Enter notes here...'.tr,
maxLines: 3,
padding: const EdgeInsets.all(12.0),
decoration: BoxDecoration(
border: Border.all(color: CupertinoColors.systemGrey),
borderRadius: BorderRadius.circular(8.0),
),
),
const SizedBox(height: 16),
Row(
children: [
Expanded(
child: MyElevatedButton(
title: 'Call Driver'.tr,
onPressed: () {
controller.makePhoneCall(driver['phone'].toString());
},
),
),
const SizedBox(width: 16),
Expanded(
child: CupertinoButton(
onPressed: () async {
await controller.addWelcomeCall(driver['id'].toString());
},
child: Text('Save Changes'.tr),
),
),
Expanded(
child: CupertinoButton(
onPressed: () async {
final phone = driver['phone'];
if (phone == null || phone.toString().isEmpty) {
Get.snackbar("خطأ", "لا يوجد رقم هاتف لهذا السائق");
return;
}
String message = "مرحباً،\n\n"
"يعطيك العافية أستاذ. نرحب بك في شركة *انطلق* للنقل الذكي.\n"
"نود تعريفك بالتطبيق ومميزاته لتتمكن من الاستفادة الكاملة وبدء العمل معنا بسهولة.\n\n"
"لأي استفسار أو مساعدة، يمكنك التواصل معنا على الأرقام التالية:\n\n"
"+963 952 475 742\n"
"+963 952 475 740\n"
"+963 952 475 734\n\n"
"فريق انطلق يتمنى لك تجربة موفقة ويوم سعيد.";
Get.find<MainController>().launchCommunication(
'whatsapp',
phone,
message,
);
},
child: Text('send'.tr),
),
),
],
),
],
),
),
);
}
}
class InfoText extends StatelessWidget {
final String label;
final dynamic value;
const InfoText(this.label, this.value, {super.key});
@override
Widget build(BuildContext context) {
final display = value?.toString() ?? 'N/A';
return Padding(
padding: const EdgeInsets.symmetric(vertical: 2.0),
child: Text(
'$label: $display',
style: CupertinoTheme.of(context).textTheme.textStyle,
),
);
}
}
class CupertinoCard extends StatelessWidget {
final Widget child;
final EdgeInsetsGeometry margin;
const CupertinoCard(
{super.key, required this.child, this.margin = EdgeInsets.zero});
@override
Widget build(BuildContext context) {
return Container(
margin: margin,
decoration: BoxDecoration(
color: CupertinoColors.systemBackground,
borderRadius: BorderRadius.circular(12.0),
boxShadow: [
BoxShadow(
color: CupertinoColors.systemGrey.withOpacity(0.2),
spreadRadius: 1,
blurRadius: 5,
offset: const Offset(0, 3),
),
],
),
child: child,
);
}
}

View File

@@ -0,0 +1,352 @@
import 'dart:convert';
import 'package:crypto/crypto.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:intl/intl.dart';
import 'package:siro_service/constant/box_name.dart';
import 'package:siro_service/constant/links.dart';
import 'package:siro_service/controller/functions/crud.dart';
import 'package:siro_service/main.dart';
import 'package:siro_service/print.dart';
import 'package:siro_service/views/widgets/my_dialog.dart';
import 'pages/registration_captain_page.dart';
class RegisterCaptainServiceController extends GetxController {
// --- UI State Management ---
var isLoading = true.obs;
late PageController pageController;
var currentPageIndex = 0.obs;
var driverId = ''.obs;
var phone = ''.obs;
var serverData = <String, dynamic>{}.obs;
// En tu archivo registration_captain_controller.dart
final phoneController = TextEditingController();
// --- Dynamic Document Image URLs ---
final Map<String, RxString> docUrls = {
'driver_license_front': ''.obs,
'driver_license_back': ''.obs,
'car_license_front': ''.obs,
'car_license_back': ''.obs,
};
// --- Text Field Controllers ---
late TextEditingController firstNameController;
late TextEditingController lastNameController;
late TextEditingController siteController;
late TextEditingController nationalNumberController;
late TextEditingController birthdateController;
late TextEditingController licenseCategoriesController;
late TextEditingController expiryDateController;
late TextEditingController ownerController;
late TextEditingController colorController;
late TextEditingController carPlateController;
late TextEditingController vinController;
late TextEditingController licenseIssueDateController;
late TextEditingController carLicenseExpiryDateController;
late TextEditingController makeController;
late TextEditingController modelController;
late TextEditingController yearController;
// --- Reactive State for Dropdowns ---
var selectedGender = ''.obs;
var colorHex = ''.obs;
var selectedFuel = ''.obs;
@override
void onInit() {
super.onInit();
pageController = PageController();
// final arguments = Get.arguments;
// driverId.value = arguments?['driverId']; // Fallback for testing
// phone.value = arguments?['phone']; // Fallback for testing
_initializeTextControllers();
_initializeDocUrls();
fetchDataFromServer();
}
void _initializeDocUrls() {
const baseUrl =
"https://syria.intaleq.xyz/intaleq/auth/syria/driversDocs/syria.intaleq.xyz-";
docUrls['driver_license_front']!.value =
"$baseUrl${driverId.value}-driver_license_front.jpg";
docUrls['driver_license_back']!.value =
"$baseUrl${driverId.value}-driver_license_back.jpg";
docUrls['car_license_front']!.value =
"$baseUrl${driverId.value}-car_license_front.jpg";
docUrls['car_license_back']!.value =
"$baseUrl${driverId.value}-car_license_back.jpg";
}
void _initializeTextControllers() {
firstNameController = TextEditingController();
lastNameController = TextEditingController();
siteController = TextEditingController();
nationalNumberController = TextEditingController();
birthdateController = TextEditingController();
licenseCategoriesController = TextEditingController();
expiryDateController = TextEditingController();
ownerController = TextEditingController();
colorController = TextEditingController();
carPlateController = TextEditingController();
vinController = TextEditingController();
licenseIssueDateController = TextEditingController();
carLicenseExpiryDateController = TextEditingController();
makeController = TextEditingController();
modelController = TextEditingController();
yearController = TextEditingController();
}
Future<void> fetchDataFromServer() async {
isLoading.value = true;
try {
var responseString = await CRUD().post(
link: AppLink.getDriverDetailsForActivate,
payload: {'driverId': driverId.value});
if (responseString != 'failure') {
var decodedResponse = responseString;
if (decodedResponse['status'] == 'success' &&
(decodedResponse['message'] as List).isNotEmpty) {
var rawData = decodedResponse['message'][0] as Map<String, dynamic>;
// Sanitize data: ensure all values are strings to prevent type errors
serverData.value = rawData
.map((key, value) => MapEntry(key, value?.toString() ?? ''));
_populateControllersWithServerData();
}
}
} catch (e) {
Get.snackbar('Error', 'An unexpected error occurred: $e');
} finally {
isLoading.value = false;
}
}
void _populateControllersWithServerData() {
firstNameController.text = serverData['first_name'] ?? '';
lastNameController.text = serverData['last_name'] ?? '';
siteController.text = serverData['site'] ?? '';
nationalNumberController.text = serverData['national_number'] ?? '';
birthdateController.text = serverData['birthdate'] ?? '';
selectedGender.value = serverData['gender'] ?? '';
licenseCategoriesController.text = serverData['license_categories'] ?? '';
expiryDateController.text = serverData['expiry_date'] ?? '';
ownerController.text = serverData['owner'] ?? '';
final serverColorName = serverData['color'] ?? '';
final colorOption = kCarColorOptions.firstWhere(
(opt) => opt['key'] == serverColorName,
orElse: () => {'key': serverColorName, 'hex': '#000000'}); // Fallback
colorController.text = colorOption['key']!;
colorHex.value = colorOption['hex']!;
carPlateController.text = serverData['car_plate'] ?? '';
vinController.text = serverData['vin'] ?? '';
licenseIssueDateController.text = serverData['issue_date'] ?? '';
carLicenseExpiryDateController.text = serverData['expiration_date'] ?? '';
makeController.text = serverData['make'] ?? '';
modelController.text = serverData['model'] ?? '';
selectedFuel.value = serverData['fuel'] ?? '';
yearController.text = serverData['year'] ?? '';
}
Future<void> selectDate(
BuildContext context, TextEditingController controller) async {
DateTime initialDate = DateTime.now();
try {
if (controller.text.isNotEmpty)
initialDate = DateFormat('yyyy-MM-dd').parse(controller.text);
} catch (e) {/* Use default if parsing fails */}
DateTime? pickedDate = initialDate;
await showCupertinoModalPopup<void>(
context: context,
builder: (BuildContext context) => Container(
height: 280,
color: CupertinoColors.systemBackground.resolveFrom(context),
child: Column(
children: [
SizedBox(
height: 200,
child: CupertinoDatePicker(
initialDateTime: initialDate,
minimumDate: DateTime(1950),
maximumDate: DateTime(2050),
mode: CupertinoDatePickerMode.date,
onDateTimeChanged: (DateTime newDate) {
pickedDate = newDate;
},
),
),
CupertinoButton(
child: Text('Done'.tr),
onPressed: () {
if (pickedDate != null)
controller.text =
DateFormat('yyyy-MM-dd').format(pickedDate!);
Navigator.of(context).pop();
},
),
],
),
),
);
}
void nextPage() {
if (currentPageIndex.value < 3)
pageController.nextPage(
duration: const Duration(milliseconds: 400), curve: Curves.easeInOut);
}
void previousPage() {
if (currentPageIndex.value > 0)
pageController.previousPage(
duration: const Duration(milliseconds: 400), curve: Curves.easeInOut);
}
void updateAndActivateSyrianDriver() {
isLoading.value = true;
var bytes = utf8.encode(phoneController.text);
// 2. Genera el hash MD5 completo
var digest = md5.convert(bytes);
// 3. Convierte el hash a un string (tendrá 32 caracteres)
String fullMd5Hash = digest.toString();
// 4. Trunca el string a los primeros 20 caracteres
driverId.value = fullMd5Hash.substring(0, 20);
Map<String, dynamic> dataToSend = {
// Driver fields from all pages
'id': driverId.value,
'phone': phoneController.text,
'first_name': firstNameController.text,
'last_name': lastNameController.text,
'site': siteController.text,
'national_number': nationalNumberController.text,
'gender': selectedGender.value,
'birthdate': birthdateController.text,
'license_categories': licenseCategoriesController.text,
'expiry_date': expiryDateController.text,
'license_issue_date': licenseIssueDateController.text,
'password': md5.convert(utf8.encode('123456')).toString(),
'status': 'actives',
// Car fields from all pages
'owner': ownerController.text,
'color': colorController.text,
'color_hex': colorHex.value,
'car_plate': carPlateController.text,
'maritalStatus': box.read(BoxName.employeename) ?? 'unknown',
// 'vin': vinController.text,
'expiration_date': carLicenseExpiryDateController.text,
'make': makeController.text,
'model': modelController.text,
'fuel': selectedFuel.value,
'year': yearController.text,
};
print("--- Submitting Data to Server ---");
print(dataToSend);
CRUD()
.post(
link:
'${AppLink.server}/serviceapp/registerDriverAndCarService.php',
payload: dataToSend)
.then((response) {
isLoading.value = false;
var decodedResponse = (response);
Log.print('decodedResponse: ${decodedResponse}');
if (decodedResponse != 'failure' && decodedResponse['status'] == 'success') {
MyDialog().getDialog('Success'.tr, '',
Text('Driver has been activated successfully!'.tr), () {
Get.back();
Get.back();
// fetchDataFromServer();
Get.back();
// Get.off(() => RegisterCaptain());
});
} else {
Get.snackbar(
'Error'.tr,
'Failed to update driver: ${decodedResponse is Map ? decodedResponse['message'] : 'failure'}'.tr,
backgroundColor: Colors.red,
colorText: Colors.white,
);
}
}).catchError((error) {
isLoading.value = false;
Get.snackbar(
'Error'.tr,
'An error occurred: $error'.tr,
backgroundColor: Colors.red,
colorText: Colors.white,
);
});
}
static const List<Map<String, String>> kCarColorOptions = [
{'key': 'white', 'hex': '#FFFFFF'},
{'key': 'black', 'hex': '#000000'},
{'key': 'silver', 'hex': '#C0C0C0'},
{'key': 'gray', 'hex': '#808080'},
{'key': 'gunmetal', 'hex': '#2A3439'},
{'key': 'red', 'hex': '#C62828'},
{'key': 'blue', 'hex': '#1565C0'},
{'key': 'navy', 'hex': '#0D47A1'},
{'key': 'green', 'hex': '#2E7D32'},
{'key': 'darkGreen', 'hex': '#1B5E20'},
{'key': 'beige', 'hex': '#D7CCC8'},
{'key': 'brown', 'hex': '#5D4037'},
{'key': 'maroon', 'hex': '#800000'},
{'key': 'burgundy', 'hex': '#800020'},
{'key': 'yellow', 'hex': '#F9A825'},
{'key': 'orange', 'hex': '#EF6C00'},
{'key': 'gold', 'hex': '#D4AF37'},
{'key': 'bronze', 'hex': '#CD7F32'},
{'key': 'champagne', 'hex': '#EFE1C6'},
{'key': 'purple', 'hex': '#6A1B9A'},
];
static const List<String> kFuelOptions = [
'بنزين',
'ديزل',
'هايبرد',
'كهربائي'
];
Color hexToColor(String code) =>
Color(int.parse(code.substring(1, 7), radix: 16) + 0xFF000000);
void updateColorSelection(String hex) {
colorHex.value = hex;
colorController.text =
kCarColorOptions.firstWhere((o) => o['hex'] == hex)['key']!;
}
@override
void onClose() {
pageController.dispose();
firstNameController.dispose();
lastNameController.dispose();
siteController.dispose();
nationalNumberController.dispose();
birthdateController.dispose();
licenseCategoriesController.dispose();
expiryDateController.dispose();
ownerController.dispose();
colorController.dispose();
carPlateController.dispose();
vinController.dispose();
licenseIssueDateController.dispose();
carLicenseExpiryDateController.dispose();
makeController.dispose();
modelController.dispose();
yearController.dispose();
super.onClose();
}
}

View File

@@ -0,0 +1,338 @@
import 'dart:convert';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:intl/intl.dart';
import 'package:siro_service/constant/box_name.dart';
import 'package:siro_service/constant/links.dart';
import 'package:siro_service/controller/functions/crud.dart';
import 'package:siro_service/main.dart';
import 'package:siro_service/print.dart';
import 'package:siro_service/views/widgets/my_dialog.dart';
import 'pages/registration_captain_page.dart';
class RegisterCaptainController extends GetxController {
// --- UI State Management ---
var isLoading = true.obs;
late PageController pageController;
var currentPageIndex = 0.obs;
var driverId = ''.obs;
var phone = ''.obs;
var serverData = <String, dynamic>{}.obs;
// En tu archivo registration_captain_controller.dart
final phoneController = TextEditingController();
// --- Dynamic Document Image URLs ---
final Map<String, RxString> docUrls = {
'driver_license_front': ''.obs,
'driver_license_back': ''.obs,
'car_license_front': ''.obs,
'car_license_back': ''.obs,
};
// --- Text Field Controllers ---
late TextEditingController firstNameController;
late TextEditingController lastNameController;
late TextEditingController siteController;
late TextEditingController nationalNumberController;
late TextEditingController birthdateController;
late TextEditingController licenseCategoriesController;
late TextEditingController expiryDateController;
late TextEditingController ownerController;
late TextEditingController colorController;
late TextEditingController carPlateController;
late TextEditingController vinController;
late TextEditingController licenseIssueDateController;
late TextEditingController carLicenseExpiryDateController;
late TextEditingController makeController;
late TextEditingController modelController;
late TextEditingController yearController;
// --- Reactive State for Dropdowns ---
var selectedGender = ''.obs;
var colorHex = ''.obs;
var selectedFuel = ''.obs;
@override
void onInit() {
super.onInit();
pageController = PageController();
final arguments = Get.arguments;
driverId.value = arguments?['driverId']; // Fallback for testing
phone.value = arguments?['phone']; // Fallback for testing
_initializeTextControllers();
_initializeDocUrls();
fetchDataFromServer();
}
void _initializeDocUrls() {
const baseUrl =
"https://syria.intaleq.xyz/intaleq/auth/syria/driversDocs/syria.intaleq.xyz-";
docUrls['driver_license_front']!.value =
"$baseUrl${driverId.value}-driver_license_front.jpg";
docUrls['driver_license_back']!.value =
"$baseUrl${driverId.value}-driver_license_back.jpg";
docUrls['car_license_front']!.value =
"$baseUrl${driverId.value}-car_license_front.jpg";
docUrls['car_license_back']!.value =
"$baseUrl${driverId.value}-car_license_back.jpg";
}
void _initializeTextControllers() {
firstNameController = TextEditingController();
lastNameController = TextEditingController();
siteController = TextEditingController();
nationalNumberController = TextEditingController();
birthdateController = TextEditingController();
licenseCategoriesController = TextEditingController();
expiryDateController = TextEditingController();
ownerController = TextEditingController();
colorController = TextEditingController();
carPlateController = TextEditingController();
vinController = TextEditingController();
licenseIssueDateController = TextEditingController();
carLicenseExpiryDateController = TextEditingController();
makeController = TextEditingController();
modelController = TextEditingController();
yearController = TextEditingController();
}
Future<void> fetchDataFromServer() async {
isLoading.value = true;
try {
var responseString = await CRUD().post(
link: AppLink.getDriverDetailsForActivate,
payload: {'driverId': driverId.value});
if (responseString != 'failure') {
var decodedResponse = responseString;
if (decodedResponse['status'] == 'success' &&
(decodedResponse['message'] as List).isNotEmpty) {
var rawData = decodedResponse['message'][0] as Map<String, dynamic>;
// Sanitize data: ensure all values are strings to prevent type errors
serverData.value = rawData
.map((key, value) => MapEntry(key, value?.toString() ?? ''));
_populateControllersWithServerData();
}
}
} catch (e) {
Get.snackbar('Error', 'An unexpected error occurred: $e');
} finally {
isLoading.value = false;
}
}
void _populateControllersWithServerData() {
firstNameController.text = serverData['first_name'] ?? '';
lastNameController.text = serverData['last_name'] ?? '';
siteController.text = serverData['site'] ?? '';
nationalNumberController.text = serverData['national_number'] ?? '';
birthdateController.text = serverData['birthdate'] ?? '';
selectedGender.value = serverData['gender'] ?? '';
licenseCategoriesController.text = serverData['license_categories'] ?? '';
expiryDateController.text = serverData['expiry_date'] ?? '';
ownerController.text = serverData['owner'] ?? '';
final serverColorName = serverData['color'] ?? '';
final colorOption = kCarColorOptions.firstWhere(
(opt) => opt['key'] == serverColorName,
orElse: () => {'key': serverColorName, 'hex': '#000000'}); // Fallback
colorController.text = colorOption['key']!;
colorHex.value = colorOption['hex']!;
carPlateController.text = serverData['car_plate'] ?? '';
vinController.text = serverData['vin'] ?? '';
licenseIssueDateController.text = serverData['issue_date'] ?? '';
carLicenseExpiryDateController.text = serverData['expiration_date'] ?? '';
makeController.text = serverData['make'] ?? '';
modelController.text = serverData['model'] ?? '';
selectedFuel.value = serverData['fuel'] ?? '';
yearController.text = serverData['year'] ?? '';
}
Future<void> selectDate(
BuildContext context, TextEditingController controller) async {
DateTime initialDate = DateTime.now();
try {
if (controller.text.isNotEmpty)
initialDate = DateFormat('yyyy-MM-dd').parse(controller.text);
} catch (e) {/* Use default if parsing fails */}
DateTime? pickedDate = initialDate;
await showCupertinoModalPopup<void>(
context: context,
builder: (BuildContext context) => Container(
height: 280,
color: CupertinoColors.systemBackground.resolveFrom(context),
child: Column(
children: [
SizedBox(
height: 200,
child: CupertinoDatePicker(
initialDateTime: initialDate,
minimumDate: DateTime(1950),
maximumDate: DateTime(2050),
mode: CupertinoDatePickerMode.date,
onDateTimeChanged: (DateTime newDate) {
pickedDate = newDate;
},
),
),
CupertinoButton(
child: Text('Done'.tr),
onPressed: () {
if (pickedDate != null)
controller.text =
DateFormat('yyyy-MM-dd').format(pickedDate!);
Navigator.of(context).pop();
},
),
],
),
),
);
}
void nextPage() {
if (currentPageIndex.value < 3)
pageController.nextPage(
duration: const Duration(milliseconds: 400), curve: Curves.easeInOut);
}
void previousPage() {
if (currentPageIndex.value > 0)
pageController.previousPage(
duration: const Duration(milliseconds: 400), curve: Curves.easeInOut);
}
void updateAndActivateSyrianDriver() {
isLoading.value = true;
Map<String, dynamic> dataToSend = {
// Driver fields from all pages
'driverId': driverId.value,
'phone': phone.value,
'first_name': firstNameController.text,
'last_name': lastNameController.text,
'site': siteController.text,
'national_number': nationalNumberController.text,
'gender': selectedGender.value,
'birthdate': '2000-01-01', //birthdateController.text,
'license_categories': licenseCategoriesController.text,
'expiry_date': expiryDateController.text,
'license_issue_date': licenseIssueDateController.text,
// Car fields from all pages
'owner': ownerController.text,
'color': colorController.text,
'color_hex': colorHex.value,
'car_plate': carPlateController.text,
'maritalStatus': box.read(BoxName.employeename) ?? 'unknown',
// 'vin': vinController.text,
'expiration_date': carLicenseExpiryDateController.text,
'make': makeController.text,
'model': modelController.text,
'fuel': selectedFuel.value,
'year': yearController.text,
};
print("--- Submitting Data to Server ---");
print(dataToSend);
CRUD()
.post(link: AppLink.updateDriverToActive, payload: dataToSend)
.then((response) {
isLoading.value = false;
var decodedResponse = (response);
Log.print('decodedResponse: ${decodedResponse}');
if (decodedResponse != 'failure' && decodedResponse['status'] == 'success') {
MyDialog().getDialog('Success'.tr, '',
Text('Driver has been activated successfully!'.tr), () {
Get.back();
Get.back();
// fetchDataFromServer();
Get.back();
// Get.off(() => RegisterCaptain());
});
} else {
Get.snackbar(
'Error'.tr,
'Failed to update driver: ${decodedResponse is Map ? decodedResponse['message'] : 'failure'}'.tr,
backgroundColor: Colors.red,
colorText: Colors.white,
);
}
}).catchError((error) {
isLoading.value = false;
Get.snackbar(
'Error'.tr,
'An error occurred: $error'.tr,
backgroundColor: Colors.red,
colorText: Colors.white,
);
});
}
static const List<Map<String, String>> kCarColorOptions = [
{'key': 'white', 'hex': '#FFFFFF'},
{'key': 'black', 'hex': '#000000'},
{'key': 'silver', 'hex': '#C0C0C0'},
{'key': 'gray', 'hex': '#808080'},
{'key': 'gunmetal', 'hex': '#2A3439'},
{'key': 'red', 'hex': '#C62828'},
{'key': 'blue', 'hex': '#1565C0'},
{'key': 'navy', 'hex': '#0D47A1'},
{'key': 'green', 'hex': '#2E7D32'},
{'key': 'darkGreen', 'hex': '#1B5E20'},
{'key': 'beige', 'hex': '#D7CCC8'},
{'key': 'brown', 'hex': '#5D4037'},
{'key': 'maroon', 'hex': '#800000'},
{'key': 'burgundy', 'hex': '#800020'},
{'key': 'yellow', 'hex': '#F9A825'},
{'key': 'orange', 'hex': '#EF6C00'},
{'key': 'gold', 'hex': '#D4AF37'},
{'key': 'bronze', 'hex': '#CD7F32'},
{'key': 'champagne', 'hex': '#EFE1C6'},
{'key': 'purple', 'hex': '#6A1B9A'},
];
static const List<String> kFuelOptions = [
'بنزين',
'ديزل',
'هايبرد',
'كهربائي'
];
Color hexToColor(String code) =>
Color(int.parse(code.substring(1, 7), radix: 16) + 0xFF000000);
void updateColorSelection(String hex) {
colorHex.value = hex;
colorController.text =
kCarColorOptions.firstWhere((o) => o['hex'] == hex)['key']!;
}
@override
void onClose() {
pageController.dispose();
firstNameController.dispose();
lastNameController.dispose();
siteController.dispose();
nationalNumberController.dispose();
birthdateController.dispose();
licenseCategoriesController.dispose();
expiryDateController.dispose();
ownerController.dispose();
colorController.dispose();
carPlateController.dispose();
vinController.dispose();
licenseIssueDateController.dispose();
carLicenseExpiryDateController.dispose();
makeController.dispose();
modelController.dispose();
yearController.dispose();
super.onClose();
}
}

View File

@@ -0,0 +1,147 @@
import 'package:flutter/material.dart';
import '../../constant/colors.dart';
import '../../constant/style.dart';
ThemeData lightThemeEnglish = ThemeData(
brightness: Brightness.light,
fontFamily: "SFPro",
textTheme: TextTheme(
displaySmall: AppStyle.title,
displayLarge: AppStyle.headTitle,
displayMedium: AppStyle.headTitle2,
bodyLarge: AppStyle.title,
bodyMedium: AppStyle.subtitle,
),
primarySwatch: Colors.blue,
dialogTheme: DialogThemeData(
backgroundColor: AppColor.secondaryColor,
contentTextStyle: AppStyle.title,
titleTextStyle: AppStyle.headTitle2,
),
appBarTheme: AppBarTheme(
elevation: 0,
color: AppColor.secondaryColor,
centerTitle: true,
iconTheme: const IconThemeData(
color: AppColor.primaryColor,
),
toolbarTextStyle: TextTheme(
titleSmall: AppStyle.subtitle,
headlineSmall: AppStyle.title,
titleLarge: AppStyle.headTitle2,
).bodyMedium,
titleTextStyle: TextTheme(
titleSmall: AppStyle.subtitle,
headlineSmall: AppStyle.title,
titleLarge: AppStyle.headTitle2,
).titleLarge,
),
);
ThemeData darkThemeEnglish = ThemeData(
brightness: Brightness.dark,
fontFamily: "SFPro",
textTheme: TextTheme(
displaySmall: AppStyle.title,
displayLarge: AppStyle.headTitle,
displayMedium: AppStyle.headTitle2,
bodyLarge: AppStyle.title,
bodyMedium: AppStyle.subtitle,
),
primarySwatch: Colors.blue,
dialogTheme: DialogThemeData(
backgroundColor: AppColor.secondaryColor,
contentTextStyle: AppStyle.title,
titleTextStyle: AppStyle.headTitle2,
),
appBarTheme: AppBarTheme(
elevation: 0,
color: AppColor.secondaryColor,
centerTitle: true,
iconTheme: const IconThemeData(
color: AppColor.primaryColor,
),
toolbarTextStyle: TextTheme(
titleSmall: AppStyle.subtitle,
headlineSmall: AppStyle.title,
titleLarge: AppStyle.headTitle2,
).bodyMedium,
titleTextStyle: TextTheme(
titleSmall: AppStyle.subtitle,
headlineSmall: AppStyle.title,
titleLarge: AppStyle.headTitle2,
).titleLarge,
),
);
ThemeData lightThemeArabic = ThemeData(
brightness: Brightness.light,
fontFamily: 'SFArabic',
textTheme: TextTheme(
displaySmall: AppStyle.title,
displayLarge: AppStyle.headTitle,
displayMedium: AppStyle.headTitle2,
bodyLarge: AppStyle.title,
bodyMedium: AppStyle.subtitle,
),
primarySwatch: Colors.blue,
dialogTheme: DialogThemeData(
backgroundColor: AppColor.secondaryColor,
contentTextStyle: AppStyle.title,
titleTextStyle: AppStyle.headTitle2,
),
appBarTheme: AppBarTheme(
elevation: 0,
color: AppColor.secondaryColor,
centerTitle: true,
iconTheme: const IconThemeData(
color: AppColor.primaryColor,
),
toolbarTextStyle: TextTheme(
titleSmall: AppStyle.subtitle,
headlineSmall: AppStyle.title,
titleLarge: AppStyle.headTitle2,
).bodyMedium,
titleTextStyle: TextTheme(
titleSmall: AppStyle.subtitle,
headlineSmall: AppStyle.title,
titleLarge: AppStyle.headTitle2,
).titleLarge,
),
);
ThemeData darkThemeArabic = ThemeData(
brightness: Brightness.dark,
fontFamily: 'SFArabic',
textTheme: TextTheme(
displaySmall: AppStyle.title,
displayLarge: AppStyle.headTitle,
displayMedium: AppStyle.headTitle2,
bodyLarge: AppStyle.title,
bodyMedium: AppStyle.subtitle,
),
primarySwatch: Colors.blue,
dialogTheme: DialogThemeData(
backgroundColor: AppColor.secondaryColor,
contentTextStyle: AppStyle.title,
titleTextStyle: AppStyle.headTitle2,
),
appBarTheme: AppBarTheme(
elevation: 0,
color: AppColor.secondaryColor,
centerTitle: true,
iconTheme: const IconThemeData(
color: AppColor.primaryColor,
),
toolbarTextStyle: TextTheme(
titleSmall: AppStyle.subtitle,
headlineSmall: AppStyle.title,
titleLarge: AppStyle.headTitle2,
).bodyMedium,
titleTextStyle: TextTheme(
titleSmall: AppStyle.subtitle,
headlineSmall: AppStyle.title,
titleLarge: AppStyle.headTitle2,
).titleLarge,
),
);

219
siro_service/lib/env/env.dart vendored Normal file
View File

@@ -0,0 +1,219 @@
import 'package:envied/envied.dart';
part 'env.g.dart';
@Envied()
abstract class Env {
@EnviedField(varName: 'addd', obfuscate: true)
static final String addd = _Env.addd;
@EnviedField(varName: 'getapiKey', obfuscate: true)
static final String getapiKey = _Env.getapiKey;
@EnviedField(varName: 'passnpassenger', obfuscate: true)
static final String passnpassenger = _Env.passnpassenger;
@EnviedField(varName: 'newId', obfuscate: true)
static final String newId = _Env.newId;
@EnviedField(varName: 'allowed', obfuscate: true)
static final String allowed = _Env.allowed;
@EnviedField(varName: 'basicAuthCredentials', obfuscate: true)
static final String basicAuthCredentials = _Env.basicAuthCredentials;
@EnviedField(varName: 'visionApi', obfuscate: true)
static final String visionApi = _Env.visionApi;
@EnviedField(varName: 'smsPasswordEgypt', obfuscate: true)
static final String smsPasswordEgypt = _Env.smsPasswordEgypt;
@EnviedField(varName: 'ocpApimSubscriptionKey', obfuscate: true)
static final String ocpApimSubscriptionKey = _Env.ocpApimSubscriptionKey;
@EnviedField(varName: 'serverPHP', obfuscate: true)
static final String serverPHP = _Env.serverPHP;
@EnviedField(varName: 'seferAlexandriaServer', obfuscate: true)
static final String seferAlexandriaServer = _Env.seferAlexandriaServer;
@EnviedField(varName: 'seferPaymentServer', obfuscate: true)
static final String seferPaymentServer = _Env.seferPaymentServer;
@EnviedField(varName: 'seferCairoServer', obfuscate: true)
static final String seferCairoServer = _Env.seferCairoServer;
@EnviedField(varName: 'seferGizaServer', obfuscate: true)
static final String seferGizaServer = _Env.seferGizaServer;
@EnviedField(varName: 'anthropicAIkeySeferNew', obfuscate: true)
static final String anthropicAIkeySeferNew = _Env.anthropicAIkeySeferNew;
@EnviedField(
varName: 'anthropicAIkeySeferNewHamzaayedpython', obfuscate: true)
static final String anthropicAIkeySeferNewHamzaayedpython =
_Env.anthropicAIkeySeferNewHamzaayedpython;
@EnviedField(varName: 'A', obfuscate: true)
static final String A = _Env.A;
@EnviedField(varName: 'B', obfuscate: true)
static final String B = _Env.B;
@EnviedField(varName: 'C', obfuscate: true)
static final String C = _Env.C;
@EnviedField(varName: 'D', obfuscate: true)
static final String D = _Env.D;
@EnviedField(varName: 'E', obfuscate: true)
static final String E = _Env.E;
@EnviedField(varName: 'F', obfuscate: true)
static final String F = _Env.F;
@EnviedField(varName: 'G', obfuscate: true)
static final String G = _Env.G;
@EnviedField(varName: 'H', obfuscate: true)
static final String H = _Env.H;
@EnviedField(varName: 'I', obfuscate: true)
static final String I = _Env.I;
@EnviedField(varName: 'J', obfuscate: true)
static final String J = _Env.J;
@EnviedField(varName: 'K', obfuscate: true)
static final String K = _Env.K;
@EnviedField(varName: 'L', obfuscate: true)
static final String L = _Env.L;
@EnviedField(varName: 'M', obfuscate: true)
static final String M = _Env.M;
@EnviedField(varName: 'N', obfuscate: true)
static final String N = _Env.N;
@EnviedField(varName: 'O', obfuscate: true)
static final String O = _Env.O;
@EnviedField(varName: 'P', obfuscate: true)
static final String P = _Env.P;
@EnviedField(varName: 'Q', obfuscate: true)
static final String Q = _Env.Q;
@EnviedField(varName: 'R', obfuscate: true)
static final String R = _Env.R;
@EnviedField(varName: 'S', obfuscate: true)
static final String S = _Env.S;
@EnviedField(varName: 'T', obfuscate: true)
static final String T = _Env.T;
@EnviedField(varName: 'U', obfuscate: true)
static final String U = _Env.U;
@EnviedField(varName: 'V', obfuscate: true)
static final String V = _Env.V;
@EnviedField(varName: 'W', obfuscate: true)
static final String W = _Env.W;
@EnviedField(varName: 'X', obfuscate: true)
static final String X = _Env.X;
@EnviedField(varName: 'Y', obfuscate: true)
static final String Y = _Env.Y;
@EnviedField(varName: 'Z', obfuscate: true)
static final String Z = _Env.Z;
@EnviedField(varName: 'a', obfuscate: true)
static final String a = _Env.a;
@EnviedField(varName: 'b', obfuscate: true)
static final String b = _Env.b;
@EnviedField(varName: 'c', obfuscate: true)
static final String c = _Env.c;
@EnviedField(varName: 'd', obfuscate: true)
static final String d = _Env.d;
@EnviedField(varName: 'e', obfuscate: true)
static final String e = _Env.e;
@EnviedField(varName: 'f', obfuscate: true)
static final String f = _Env.f;
@EnviedField(varName: 'g', obfuscate: true)
static final String g = _Env.g;
@EnviedField(varName: 'h', obfuscate: true)
static final String h = _Env.h;
@EnviedField(varName: 'i', obfuscate: true)
static final String i = _Env.i;
@EnviedField(varName: 'j', obfuscate: true)
static final String j = _Env.j;
@EnviedField(varName: 'k', obfuscate: true)
static final String k = _Env.k;
@EnviedField(varName: 'l', obfuscate: true)
static final String l = _Env.l;
@EnviedField(varName: 'm', obfuscate: true)
static final String m = _Env.m;
@EnviedField(varName: 'n', obfuscate: true)
static final String n = _Env.n;
@EnviedField(varName: 'o', obfuscate: true)
static final String o = _Env.o;
@EnviedField(varName: 'p', obfuscate: true)
static final String p = _Env.p;
@EnviedField(varName: 'q', obfuscate: true)
static final String q = _Env.q;
@EnviedField(varName: 'r', obfuscate: true)
static final String r = _Env.r;
@EnviedField(varName: 's', obfuscate: true)
static final String s = _Env.s;
@EnviedField(varName: 't', obfuscate: true)
static final String t = _Env.t;
@EnviedField(varName: 'u', obfuscate: true)
static final String u = _Env.u;
@EnviedField(varName: 'v', obfuscate: true)
static final String v = _Env.v;
@EnviedField(varName: 'w', obfuscate: true)
static final String w = _Env.w;
@EnviedField(varName: 'x', obfuscate: true)
static final String x = _Env.x;
@EnviedField(varName: 'y', obfuscate: true)
static final String y = _Env.y;
@EnviedField(varName: 'z', obfuscate: true)
static final String z = _Env.z;
@EnviedField(varName: 'emailService', obfuscate: true)
static final String emailService = _Env.emailService;
@EnviedField(varName: 'keyOfApp', obfuscate: true)
static final String keyOfApp = _Env.keyOfApp;
@EnviedField(varName: 'initializationVector', obfuscate: true)
static final String initializationVector = _Env.initializationVector;
}

2137
siro_service/lib/env/env.g.dart vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,67 @@
// File generated by FlutterFire CLI.
// ignore_for_file: type=lint
import 'package:firebase_core/firebase_core.dart' show FirebaseOptions;
import 'package:flutter/foundation.dart'
show defaultTargetPlatform, kIsWeb, TargetPlatform;
/// Default [FirebaseOptions] for use with your Firebase apps.
///
/// Example:
/// ```dart
/// import 'firebase_options.dart';
/// // ...
/// await Firebase.initializeApp(
/// options: DefaultFirebaseOptions.currentPlatform,
/// );
/// ```
class DefaultFirebaseOptions {
static FirebaseOptions get currentPlatform {
if (kIsWeb) {
throw UnsupportedError(
'DefaultFirebaseOptions have not been configured for web - '
'you can reconfigure this by running the FlutterFire CLI again.',
);
}
switch (defaultTargetPlatform) {
case TargetPlatform.android:
return android;
case TargetPlatform.iOS:
return ios;
case TargetPlatform.macOS:
throw UnsupportedError(
'DefaultFirebaseOptions have not been configured for macos - '
'you can reconfigure this by running the FlutterFire CLI again.',
);
case TargetPlatform.windows:
throw UnsupportedError(
'DefaultFirebaseOptions have not been configured for windows - '
'you can reconfigure this by running the FlutterFire CLI again.',
);
case TargetPlatform.linux:
throw UnsupportedError(
'DefaultFirebaseOptions have not been configured for linux - '
'you can reconfigure this by running the FlutterFire CLI again.',
);
default:
throw UnsupportedError(
'DefaultFirebaseOptions are not supported for this platform.',
);
}
}
static const FirebaseOptions android = FirebaseOptions(
apiKey: 'AIzaSyAEoply_UcEP6KaCu_ziCy_ZDIjAKvi7b8',
appId: '1:825988584191:android:be08180beef7d65e1632ca',
messagingSenderId: '825988584191',
projectId: 'siro-a6957',
storageBucket: 'siro-a6957.firebasestorage.app',
);
static const FirebaseOptions ios = FirebaseOptions(
apiKey: 'AIzaSyDk6x6KZUY0IQtxoCMXX0F7N_yik8O19eA',
appId: '1:825988584191:ios:9d50158dc7a09e851632ca',
messagingSenderId: '825988584191',
projectId: 'siro-a6957',
storageBucket: 'siro-a6957.firebasestorage.app',
iosBundleId: 'com.siro.service',
);
}

View File

@@ -0,0 +1,172 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:siro_service/constant/colors.dart';
import 'package:siro_service/views/auth/register_page.dart';
import 'package:siro_service/views/widgets/my_textField.dart';
import 'controller/login_controller.dart';
class LoginPage extends StatelessWidget {
const LoginPage({super.key});
@override
Widget build(BuildContext context) {
final LoginController controller = Get.put(LoginController());
return Scaffold(
backgroundColor: Colors.white,
body: Stack(
children: [
// Background Gradient Element
Positioned(
top: -100,
right: -100,
child: Container(
width: 300,
height: 300,
decoration: BoxDecoration(
shape: BoxShape.circle,
gradient: LinearGradient(
colors: [AppColor.blueColor.withOpacity(0.2), Colors.transparent],
begin: Alignment.topRight,
end: Alignment.bottomLeft,
),
),
),
),
SafeArea(
child: SingleChildScrollView(
padding: const EdgeInsets.symmetric(horizontal: 30.0),
child: Form(
key: controller.formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
const SizedBox(height: 80),
// App Logo or Icon
const Center(
child: Icon(
Icons.support_agent_rounded,
size: 100,
color: AppColor.blueColor,
),
),
const SizedBox(height: 24),
const Center(
child: Text(
'انطلق سيرفس',
style: TextStyle(
fontSize: 28,
fontWeight: FontWeight.bold,
color: Colors.black87,
letterSpacing: 1.2,
),
),
),
const Center(
child: Text(
'نظام خدمة العملاء المتكامل',
style: TextStyle(
fontSize: 16,
color: Colors.grey,
),
),
),
const SizedBox(height: 60),
// Fields with modern styling
const Text(
'تسجيل الدخول',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.w600,
color: Colors.black87,
),
),
const SizedBox(height: 20),
MyTextForm(
controller: controller.email,
label: 'البريد الإلكتروني',
hint: 'أدخل البريد الإلكتروني',
type: TextInputType.emailAddress,
),
const SizedBox(height: 20),
MyTextForm(
controller: controller.password,
label: 'كلمة المرور',
hint: 'أدخل كلمة المرور',
type: TextInputType.visiblePassword,
),
const SizedBox(height: 40),
// Login Button
Container(
height: 55,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(16),
gradient: const LinearGradient(
colors: [AppColor.blueColor, Color(0xFF1A237E)],
),
boxShadow: [
BoxShadow(
color: AppColor.blueColor.withOpacity(0.3),
blurRadius: 12,
offset: const Offset(0, 6),
),
],
),
child: Material(
color: Colors.transparent,
child: InkWell(
borderRadius: BorderRadius.circular(16),
onTap: () => controller.login(),
child: const Center(
child: Text(
'دخول',
style: TextStyle(
color: Colors.white,
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
),
),
),
),
const SizedBox(height: 30),
// Register Link
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Text(
'ليس لديك حساب موظف؟ ',
style: TextStyle(color: Colors.grey),
),
TextButton(
onPressed: () => Get.to(() => const RegisterPage()),
child: const Text(
'إنشاء حساب جديد',
style: TextStyle(
color: AppColor.blueColor,
fontWeight: FontWeight.bold,
),
),
),
],
),
],
),
),
),
),
],
),
);
}
}

View File

@@ -0,0 +1,49 @@
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:get/get.dart';
import 'package:get_storage/get_storage.dart';
import 'package:siro_service/controller/firbase_messge.dart';
import 'package:siro_service/firebase_options.dart';
import 'controller/functions/encrypt_decrypt.dart';
import 'controller/local/local_controller.dart';
import 'controller/local/translations.dart';
import 'login_page.dart';
final box = GetStorage();
const storage = FlutterSecureStorage();
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await GetStorage.init();
await EncryptionHelper.initialize();
if (Firebase.apps.isEmpty) {
await Firebase.initializeApp(
options: DefaultFirebaseOptions.currentPlatform);
} else {
Firebase.app();
}
Get.put(FirebaseMessagesController()).getToken();
runApp(MyApp());
}
class MyApp extends StatelessWidget {
MyApp({super.key});
LocaleController localController = Get.put(LocaleController());
@override
Widget build(BuildContext context) {
return GetMaterialApp(
title: 'Inatleq Service'.tr,
debugShowCheckedModeBanner: false,
translations: MyTranslation(),
locale: localController.language,
theme: localController.appTheme,
key: UniqueKey(),
home: LoginPage(),
);
}
}

View File

@@ -0,0 +1,115 @@
## Site
IP Address: 92.113.25.174
Domain Name: https://sefer.click
Site User: sefer
Password: katm5g2LLY55a7SJcnxK
## Database
Host: 127.0.0.1
Port: 3306
Database Name: sefer
Database User Name: sefer
Database User Password: la7CdYFwa9uxwnozk7ok
## WordPress
Admin E-Mail: admin@sefer.click
Admin User Name: admin
Admin Password: F12WeBpJLY4z1qHsFqMt
Admin Url: https://sefer.click/wp-admin/
ssh password is malDEV@2101mehmet
giza hostinger kvm2 pass gizaDEV@2101
contabo cloud panel new
Site
---
IP Address: 100.42.191.131
Domain Name: https://seferalexandria.site
Site User: seferalexandria
Password: x91LgGr2Q8IervoVgcgc
## Database
Host: 127.0.0.1
Port: 3306
Database Name: seferalexandria
Database User Name: seferalexandria
Database User Password: 812UvStGfCfNLMCsRR9a
## WordPress
Admin E-Mail: admin@seferalexandria.site
Admin User Name: admin
Admin Password: N9gv90TdnoOdjVch5V3J
Admin Url: https://seferalexandria.site/wp-admin/
##### alexandria
86IU5xU2HSiNNSAU5cwF username db ==>> alexandriadbUsername vps 2 100.42.191.131
- JCndq5xXLnoYOTuRqnZA for ssh alexandria ssh user
seferalexandria userAdmin cloudPanel 49prvH5MGGupjWrDPLGm
# cloudpanel username is sefercairo and pass is malDEV@2101
#####
oMnEK3IbvCbQ5G5FfK9W username db ==>> seferdbUsername vps 3 185.209.230.169 # db name is sefercairodb
H6JIiEOpyDVoMBBi5cCH sefercairoadmin
Q6MikBy4ktZn9zumyPli sefercairo ssh user 85.209.230.169
# cloudpanel username is sefercairo and pass is malDEV@2101
##### wallet
qG6Bn1P9IRvwk0tLW85v username db ==>> walletdbUsername vps 2 156.67.82.188 # db name is seferPaymentDB
- JCndq5xXLnoYOTuRqnZA for ssh wallet ssh user
seferwallet userAdmin cloudPanel 49prvH5MGGupjWrDPLGm
# cloudpanel username is seferpayment and pass is dKsmZkbdWP9hj13qjNiY and seferpayment@sefer.live
#####
##### giza
TE6VUrMcjnIsipqP9yQj username db ==>> gizaUsername vps 1 31.220.94.107 db name is sefergizaDB
- rylDg624ifiDXsWmaMST for ssh sefergizacp ssh user
# cloudpanel username is sefergiza and pass is 4O2IUSbwZxVnMJYaRTog gizasefer.online
####
#AaPOFHEwHVyXG6AgZ7i password test kvm1
4zB9qoYfxCn7uHx7i5cgUl7JR password contabo vps 3 alexandria
SLqWj2QTD66Gr password contabo vps 1 payment
uREd8QKKzpHXGL25UVi3quK password contabo vps 2 cairo sefer.click user cloud panel is
uREd8QKKzpHXGL25UVi3quK password contabo vps 1 payment seferpw.website user cloud panel is seferpayment
C3vGlIV7VM0priWXgdzc password contabo vps 2 giza seferpw.website user cloud panel is seferpayment
==================================================================
Congratulations! Installed successfully!
==================================================================
webmin Internal Address: https://31.220.94.106:10000/
username: seferpayment
password: d7b064c3
Warning:
If you cannot access the panel,
release the following port (32368|888|80|443|20|21) in the security group
Database user profile
User:
Password: 38d7a66a0e435
==================================================================

208
siro_service/lib/models/sql Normal file
View File

@@ -0,0 +1,208 @@
-- to check duplicate CarRegistration
SELECT
`driverID`,
COUNT(*) AS `count`,created_at
FROM
`CarRegistration`
GROUP BY
`driverID`
HAVING
COUNT(*) > 1;
--
-- to delete duplicate
WITH CTE AS (
SELECT
MIN(`id`) AS `min_id`
FROM
`CarRegistration`
GROUP BY
`driverID`
)
DELETE FROM
`CarRegistration`
WHERE
`id` NOT IN (SELECT `min_id` FROM CTE);
-- get for employee
SELECT
d.`maritalStatus` AS NAME,
COUNT(*) AS `count`
FROM
`driver` d
WHERE
d.`maritalStatus` IN('Maryam', 'rawda', 'Mena') AND DATE(d.created_at) = CURDATE()
GROUP BY
d.`maritalStatus`
ORDER BY
COUNT
DESC
-- get driver without cars
SELECT
d.id, d.phone
FROM
`driver` d
WHERE
d.id NOT IN (SELECT driverID FROM CarRegistration);
-- car without drivers
SELECT
cr.created_at, cr.driverID
FROM
`CarRegistration` cr
WHERE
cr.driverID NOT IN (SELECT id FROM driver);
----- driver
SELECT phone,email,name_arabic,national_number FROM `driver` WHERE national_number ='29209290106392'
ORDER BY `driver`.`created_at` DESC
------- driver work
SELECT
COUNT(`car_locations`.driver_id),
driver.id,
driver.phone,
driver.name_arabic
FROM
`car_locations`
LEFT JOIN driver ON driver.id = car_locations.driver_id
WHERE
`car_locations`. created_at > TIMESTAMP(
DATE_SUB(NOW(), INTERVAL 10 MINUTE))
GROUP BY
driver_id
ORDER BY
COUNT(driver_id)
DESC
;
------ get count of year cars
SELECT
CASE
WHEN `year` > 2017 THEN 'After 2017'
WHEN `year` BETWEEN 2000 AND 2016 THEN '2000-2016'
ELSE 'Before 2000'
END AS `year_group`,
COUNT(*) AS `count`
FROM `carPlateEdit`
GROUP BY `year_group`
ORDER BY `year_group` ASC;
-- delete location 1 day
DELETE
FROM
`car_locations`
WHERE
DATE(`created_at`) < CURDATE();
SELECT
COUNT(`car_locations`.driver_id),
driver.id,
driver.phone,
driver.name_arabic
FROM
`car_locations`
LEFT JOIN driver ON driver.id = car_locations.driver_id
WHERE
`car_locations`.created_at > TIMESTAMP(
DATE_SUB(NOW(), INTERVAL 10 MINUTE))
GROUP BY
driver_id
ORDER BY
COUNT(driver_id)
DESC
;
-- driver register by hours for employee
SELECT
d.`maritalStatus` AS NAME,
HOUR(d.created_at) AS hour,
COUNT(*) AS `count`
FROM
`driver` d
WHERE
d.`maritalStatus` IN ('Maryam', 'rawda', 'Mena')
AND DATE(d.created_at) = CURDATE()
GROUP BY
d.`maritalStatus`, HOUR(d.created_at)
ORDER BY
hour, `count` DESC;
---- monthly
SELECT
MONTH(d.created_at) AS month,
COUNT(d.id) AS `count`
FROM
`driver` d
WHERE
YEAR(d.created_at) = YEAR(CURDATE())
GROUP BY
MONTH(d.created_at)
ORDER BY
month,
`count`
DESC
;
-----AI request
SELECT
f.id,
f.passengerId,
f.feedBack,
f.datecreated,
r.id AS ride_id,
r.start_location,
r.end_location,
r.date,
r.price,
r.status,
r.paymentMethod,
r.distance,
r.carType,
r.rideTimeFinish,
r.rideTimeStart,
r.DriverIsGoingToPassenger,
COUNT(rp.id) AS countRateFromPassengerToDrivers,
COUNT(rd.id) AS countRateFromDriverToPassengers,
MAX(rp.rating) AS rateFromPassengerToDriver,
MAX(rd.rating) AS rateFromDriversToPassengers,
MAX(rp.comment) AS commentFromPassengerToDriver,
MAX(rd.comment) AS commentFromDriverToPassenger
FROM
`feedBack` f
LEFT JOIN ride r ON
r.passenger_id = f.passengerId
LEFT JOIN ratingPassenger rp ON
rp.passenger_id = r.passenger_id
LEFT JOIN ratingDriver rd ON
rd.driver_id = r.driver_id
WHERE
r.passenger_id = '113172279072358305645'
GROUP BY
f.id, f.passengerId, f.feedBack, f.datecreated,
r.id, r.start_location, r.end_location, r.date, r.price,
r.status, r.paymentMethod, r.distance, r.carType,
r.rideTimeFinish, r.rideTimeStart, r.DriverIsGoingToPassenger
ORDER BY
r.date DESC
LIMIT 1;

View File

@@ -0,0 +1,99 @@
server {
listen 80;
listen [::]:80;
listen 443 ssl http2;
listen [::]:443 ssl http2;
{{ssl_certificate_key}}
{{ssl_certificate}}
server_name www.sefer.click;
return 301 https://sefer.click$request_uri;
}
server {
listen 80;
listen [::]:80;
listen 443 ssl http2;
listen [::]:443 ssl http2;
{{ssl_certificate_key}}
{{ssl_certificate}}
server_name sefer.click www1.sefer.click;
{{root}}
{{nginx_access_log}}
{{nginx_error_log}}
# Set the maximum request body size
client_max_body_size 10m;
if ($scheme != "https") {
rewrite ^ https://$host$uri permanent;
}
location ~ /.well-known {
auth_basic off;
allow all;
}
# Security headers
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline';" always;
add_header Referrer-Policy "no-referrer-when-downgrade" always;
add_header Permissions-Policy "geolocation=(), microphone=()" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
{{settings}}
location / {
{{varnish_proxy_pass}}
proxy_set_header Host $http_host;
proxy_set_header X-Forwarded-Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_hide_header X-Varnish;
proxy_redirect off;
proxy_max_temp_file_size 0;
proxy_connect_timeout 720;
proxy_send_timeout 720;
proxy_read_timeout 720;
proxy_buffer_size 128k;
proxy_buffers 4 256k;
proxy_busy_buffers_size 256k;
proxy_temp_file_write_size 256k;
}
location ~* ^.+\.(css|js|jpg|jpeg|gif|png|ico|gz|svg|svgz|ttf|otf|woff|woff2|eot|mp4|ogg|ogv|webm|webp|zip|swf|map|mjs)$ {
add_header Access-Control-Allow-Origin "\*";
expires max;
access_log off;
}
location ~ /\.(ht|svn|git) {
deny all;
}
if (-f $request_filename) {
break;
}
}
server {
listen 8080;
listen [::]:8080;
server_name sefer.click www1.sefer.click;
{{root}}
try_files $uri $uri/ /index.php?$args;
index index.php index.html;
location ~ \.php$ {
include fastcgi_params;
fastcgi_intercept_errors on;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
try_files $uri =404;
fastcgi_read_timeout 3600;
fastcgi_send_timeout 3600;
proxy_read_timeout 3600;
proxy_send_timeout 3600;
fastcgi_param HTTPS "on";
fastcgi_param SERVER_PORT 443;
fastcgi_pass 127.0.0.1:{{php_fpm_port}};
fastcgi_param PHP_VALUE "{{php_settings}}";
}
if (-f $request_filename) {
break;
}
}
//above for cairo server

View File

@@ -0,0 +1,13 @@
import 'dart:developer' as developer;
class Log {
Log._();
static void print(String value, {StackTrace? stackTrace}) {
developer.log(value, name: 'LOG', stackTrace: stackTrace);
}
static Object? inspect(Object? object) {
return developer.inspect(object);
}
}

View File

@@ -0,0 +1,222 @@
{
"Insert Name of Driver": "",
"raw gray": "",
"Expiration Date": "",
"Insert site of Driver": "",
"Insert Name of Owner": "",
"Insert car_type of Driver": "",
"car_license_front": "",
"Alexandria": "",
"navy": "",
"Displacement": "",
"Insert birth_date of Driver": "",
"Criminal Record": "",
"Model": "",
"License Type": "",
"Birthdate": "",
"Notes": "",
"s License": "",
"Complaints": "",
"Full Name": "",
"Next": "",
"Full Name (Marital)": "",
"Non Egypt": "",
"Giza": "",
";\n static const String stripePublishableKey = ": "",
"magenta": "",
"An error occurred while saving driver data": "",
"Email": "",
"My documents have expired": "",
"Vehicle Information": "",
"Education": "",
"Capture an Image of Your ID Document Back": "",
"Personal Information": "",
"Vehicle Details Front": "",
"Tax Expiry Date": "",
"Insert site of Owner": "",
"ID Documents Back": "",
"settings": "",
"car_license_back": "",
"OK": "",
"Price": "",
"Country": "",
"orange": "",
"pink": "",
"DOB": "",
"Insert license type of Driver": "",
"Card ID": "",
"inspection\",\n \"Capture an Image of Your car license back\":\n \"Capturez une image de votre permis de voiture\",\n \"Capture an Image of Your Drivers License\":\n \"Capturez une image de votre permis de conduire\",\n \"Sign in with Google for easier email and name entry\":\n \"Connectez-vous avec Google pour faciliter la saisie de votre adresse e-mail et de votre nom\",\n \"You will choose allow all the time to be ready receive orders\":\n \"Vous choisirez de permettre à tout moment d": "",
"are you sure to pay to this driver gift": "",
"gold": "",
">Arabic (Egypt)</span>\n </div>\n </div>\n <span title=": "",
"Name (Arabic)": "",
"Driver Information": "",
"Success": "",
"insert passenger phone": "",
"yellow": "",
"Phone": "",
"National ID": "",
"thanks": "",
"Make": "",
"Not found any image": "",
"Save Notes": "",
"cyan": "",
"Insert registration_date of Car": "",
"Bank Information": "",
"best driver": "",
"indigo": "",
"peach": "",
"Ride ID": "",
"Edit car details": "",
"IssueDate": "",
"Fuel Type": "",
"Birth Date": "",
"Marital Status": "",
"Color": "",
"Name": "",
"Created At": "",
"Registration Date": "",
"View complaint": "",
"Enter notes after call": "",
"silver": "",
"Phone Number": "",
"Cropper": "",
"End Time": "",
"Enter notes here...": "",
"ID Documents Front": "",
"unknown_document": "",
": ": "",
"CardID": "",
"Insert national ID of Driver": "",
"Insert car_model of Driver": "",
"mocha": "",
"Capture an Image of Your Driver License": "",
"maroon": "",
"InspectionResult": "",
"Expired Drivers License": "",
"Failed to save driver data": "",
"Upload": "",
"Wallet Information": "",
"Drivers Cant Register": "",
"For Egypt": "",
"Driver Statistics": "",
"Hand Inserting": "",
"navy blue": "",
"Add car": "",
"Bank Code": "",
"Please enter a valid email.": "",
"Religion": "",
"Welcome call": "",
"Non-Egyptian ID Front": "",
"FullName": "",
"Call Driver": "",
"Occupation": "",
"Plate Number": "",
"Inspection Date": "",
"turquoise": "",
"Date": "",
"red ochre": "",
"gray": "",
"Edit car plate": "",
"tawny": "",
"License Categories": "",
"car plate": "",
"Image Upload Failed": "",
"incorrect_document_title": "",
"Expiry Date": "",
"Gender": "",
"Owner Name": "",
"License Information": "",
"Sefer Service": "",
"Add": "",
"expected": "",
"Name (English)": "",
"Distance": "",
"Capture an Image of Your car license back": "",
"Account Number": "",
"I don't have a suitable vehicle": "",
"black": "",
"First Name": "",
"Total Rides": "",
"NationalID": "",
"Avg Passenger Rating": "",
"Notes:": "",
"teal": "",
"National Number": "",
"green": "",
"Chassis": "",
"Insert year of Car": "",
"Average Rating": "",
"Driver details by phone": "",
"Insert all fields": "",
"champagne": "",
"Add Driver Who Wants to Work": "",
"Add Car Who Wants to Work": "",
"I'm not ready yet": "",
"Car Type": "",
"Capture an Image of Your ID Document front": "",
"Capture an Image of Your Criminal Record": "",
"License Expiry Date": "",
"blue": "",
"Done": "",
"Passengers Cant Register": "",
"Fuel": "",
"Total Payments": "",
"red": "",
"Cancel": "",
"Additional comments": "",
"Vehicle Details Back": "",
"detected": "",
"violet": "",
"Capture Image of Non-Egyptian ID Front": "",
"lime": "",
"I need more help understanding the app": "",
"id_card_front": "",
"Payment Method": "",
"Ok": "",
"Wallet Balance": "",
"Last Payment Amount": "",
"Owner": "",
"Order": "",
"Year": "",
"Capture an Image of Your car license front": "",
"Scam Reports": "",
"Welcome Drivers": "",
"Search by phone number": "",
"verdi": "",
"Please enter a valid phone number.": "",
"Save Changes": "",
"Employment": "",
"Issue Date": "",
"passenger details by phone": "",
"Insert car_number of Driver": "",
"hand inserting": "",
"driver_license": "",
"Error": "",
"Car Details": "",
"uploaded sucssefuly": "",
"Start Time": "",
"I'll register when the app is fully launched": "",
"Passport No": "",
"Please enter": "",
"Address": "",
"Documents check": "",
"Categories": "",
"Status": "",
"insert Driver phone": "",
"brown": "",
"Best Drivers": "",
"Apply Ride": "",
"Latest Ride": "",
"purple": "",
"No Car found yet": "",
"Insert phone of Driver": "",
"Passengers Rated": "",
"Last Payment Method": "",
"Insert phone of Owner": "",
"VIN": "",
"bronze": "",
"dark blue": "",
"white": "",
"id_card_back": ""
}

View File

@@ -0,0 +1,222 @@
{
"Insert Name of Driver": "",
"raw gray": "",
"Expiration Date": "",
"Insert site of Driver": "",
"Insert Name of Owner": "",
"Insert car_type of Driver": "",
"car_license_front": "",
"Alexandria": "",
"navy": "",
"Displacement": "",
"Insert birth_date of Driver": "",
"Criminal Record": "",
"Model": "",
"License Type": "",
"Birthdate": "",
"Notes": "",
"s License": "",
"Complaints": "",
"Full Name": "",
"Next": "",
"Full Name (Marital)": "",
"Non Egypt": "",
"Giza": "",
";\n static const String stripePublishableKey = ": "",
"magenta": "",
"An error occurred while saving driver data": "",
"Email": "",
"My documents have expired": "",
"Vehicle Information": "",
"Education": "",
"Capture an Image of Your ID Document Back": "",
"Personal Information": "",
"Vehicle Details Front": "",
"Tax Expiry Date": "",
"Insert site of Owner": "",
"ID Documents Back": "",
"settings": "",
"car_license_back": "",
"OK": "",
"Price": "",
"Country": "",
"orange": "",
"pink": "",
"DOB": "",
"Insert license type of Driver": "",
"Card ID": "",
"inspection\",\n \"Capture an Image of Your car license back\":\n \"Capturez une image de votre permis de voiture\",\n \"Capture an Image of Your Drivers License\":\n \"Capturez une image de votre permis de conduire\",\n \"Sign in with Google for easier email and name entry\":\n \"Connectez-vous avec Google pour faciliter la saisie de votre adresse e-mail et de votre nom\",\n \"You will choose allow all the time to be ready receive orders\":\n \"Vous choisirez de permettre à tout moment d": "",
"are you sure to pay to this driver gift": "",
"gold": "",
">Arabic (Egypt)</span>\n </div>\n </div>\n <span title=": "",
"Name (Arabic)": "",
"Driver Information": "",
"Success": "",
"insert passenger phone": "",
"yellow": "",
"Phone": "",
"National ID": "",
"thanks": "",
"Make": "",
"Not found any image": "",
"Save Notes": "",
"cyan": "",
"Insert registration_date of Car": "",
"Bank Information": "",
"best driver": "",
"indigo": "",
"peach": "",
"Ride ID": "",
"Edit car details": "",
"IssueDate": "",
"Fuel Type": "",
"Birth Date": "",
"Marital Status": "",
"Color": "",
"Name": "",
"Created At": "",
"Registration Date": "",
"View complaint": "",
"Enter notes after call": "",
"silver": "",
"Phone Number": "",
"Cropper": "",
"End Time": "",
"Enter notes here...": "",
"ID Documents Front": "",
"unknown_document": "",
": ": "",
"CardID": "",
"Insert national ID of Driver": "",
"Insert car_model of Driver": "",
"mocha": "",
"Capture an Image of Your Driver License": "",
"maroon": "",
"InspectionResult": "",
"Expired Drivers License": "",
"Failed to save driver data": "",
"Upload": "",
"Wallet Information": "",
"Drivers Cant Register": "",
"For Egypt": "",
"Driver Statistics": "",
"Hand Inserting": "",
"navy blue": "",
"Add car": "",
"Bank Code": "",
"Please enter a valid email.": "",
"Religion": "",
"Welcome call": "",
"Non-Egyptian ID Front": "",
"FullName": "",
"Call Driver": "",
"Occupation": "",
"Plate Number": "",
"Inspection Date": "",
"turquoise": "",
"Date": "",
"red ochre": "",
"gray": "",
"Edit car plate": "",
"tawny": "",
"License Categories": "",
"car plate": "",
"Image Upload Failed": "",
"incorrect_document_title": "",
"Expiry Date": "",
"Gender": "",
"Owner Name": "",
"License Information": "",
"Sefer Service": "",
"Add": "",
"expected": "",
"Name (English)": "",
"Distance": "",
"Capture an Image of Your car license back": "",
"Account Number": "",
"I don't have a suitable vehicle": "",
"black": "",
"First Name": "",
"Total Rides": "",
"NationalID": "",
"Avg Passenger Rating": "",
"Notes:": "",
"teal": "",
"National Number": "",
"green": "",
"Chassis": "",
"Insert year of Car": "",
"Average Rating": "",
"Driver details by phone": "",
"Insert all fields": "",
"champagne": "",
"Add Driver Who Wants to Work": "",
"Add Car Who Wants to Work": "",
"I'm not ready yet": "",
"Car Type": "",
"Capture an Image of Your ID Document front": "",
"Capture an Image of Your Criminal Record": "",
"License Expiry Date": "",
"blue": "",
"Done": "",
"Passengers Cant Register": "",
"Fuel": "",
"Total Payments": "",
"red": "",
"Cancel": "",
"Additional comments": "",
"Vehicle Details Back": "",
"detected": "",
"violet": "",
"Capture Image of Non-Egyptian ID Front": "",
"lime": "",
"I need more help understanding the app": "",
"id_card_front": "",
"Payment Method": "",
"Ok": "",
"Wallet Balance": "",
"Last Payment Amount": "",
"Owner": "",
"Order": "",
"Year": "",
"Capture an Image of Your car license front": "",
"Scam Reports": "",
"Welcome Drivers": "",
"Search by phone number": "",
"verdi": "",
"Please enter a valid phone number.": "",
"Save Changes": "",
"Employment": "",
"Issue Date": "",
"passenger details by phone": "",
"Insert car_number of Driver": "",
"hand inserting": "",
"driver_license": "",
"Error": "",
"Car Details": "",
"uploaded sucssefuly": "",
"Start Time": "",
"I'll register when the app is fully launched": "",
"Passport No": "",
"Please enter": "",
"Address": "",
"Documents check": "",
"Categories": "",
"Status": "",
"insert Driver phone": "",
"brown": "",
"Best Drivers": "",
"Apply Ride": "",
"Latest Ride": "",
"purple": "",
"No Car found yet": "",
"Insert phone of Driver": "",
"Passengers Rated": "",
"Last Payment Method": "",
"Insert phone of Owner": "",
"VIN": "",
"bronze": "",
"dark blue": "",
"white": "",
"id_card_back": ""
}

View File

@@ -0,0 +1,172 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:siro_service/constant/colors.dart';
import 'package:siro_service/controller/auth/register_controller.dart';
import 'package:siro_service/views/widgets/my_textField.dart';
class RegisterPage extends StatelessWidget {
const RegisterPage({super.key});
@override
Widget build(BuildContext context) {
final RegisterController controller = Get.put(RegisterController());
return Scaffold(
backgroundColor: const Color(0xFFF8F9FD),
appBar: AppBar(
backgroundColor: Colors.transparent,
elevation: 0,
leading: IconButton(
icon: const Icon(Icons.arrow_back_ios_new, color: Colors.black87),
onPressed: () => Get.back(),
),
title: Text(
'إنشاء حساب موظف'.tr,
style: const TextStyle(color: Colors.black87, fontWeight: FontWeight.bold),
),
centerTitle: true,
),
body: SafeArea(
child: SingleChildScrollView(
padding: const EdgeInsets.symmetric(horizontal: 24.0, vertical: 16.0),
child: Form(
key: controller.formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
const Center(
child: Icon(
Icons.person_add_rounded,
size: 80,
color: AppColor.blueColor,
),
),
const SizedBox(height: 32),
_buildFieldTitle('الاسم الأول'),
MyTextForm(
controller: controller.firstName,
label: 'الاسم الأول',
hint: 'أدخل الاسم الأول',
type: TextInputType.name,
),
const SizedBox(height: 16),
_buildFieldTitle('الاسم الأخير'),
MyTextForm(
controller: controller.lastName,
label: 'الاسم الأخير',
hint: 'أدخل الاسم الأخير',
type: TextInputType.name,
),
const SizedBox(height: 16),
_buildFieldTitle('البريد الإلكتروني'),
MyTextForm(
controller: controller.email,
label: 'البريد الإلكتروني',
hint: 'example@mail.com',
type: TextInputType.emailAddress,
),
const SizedBox(height: 16),
_buildFieldTitle('رقم الهاتف'),
MyTextForm(
controller: controller.phone,
label: 'رقم الهاتف',
hint: '09xxxxxxxx',
type: TextInputType.phone,
),
const SizedBox(height: 16),
_buildFieldTitle('كلمة المرور'),
MyTextForm(
controller: controller.password,
label: 'كلمة المرور',
hint: '********',
type: TextInputType.visiblePassword,
),
const SizedBox(height: 40),
Obx(() => controller.isLoading.value
? const Center(child: CircularProgressIndicator())
: Container(
height: 55,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(16),
gradient: const LinearGradient(
colors: [AppColor.blueColor, Color(0xFF1A237E)],
begin: Alignment.centerLeft,
end: Alignment.centerRight,
),
boxShadow: [
BoxShadow(
color: AppColor.blueColor.withOpacity(0.3),
blurRadius: 12,
offset: const Offset(0, 6),
),
],
),
child: Material(
color: Colors.transparent,
child: InkWell(
borderRadius: BorderRadius.circular(16),
onTap: () => controller.register(),
child: const Center(
child: Text(
'تقديم طلب تسجيل',
style: TextStyle(
color: Colors.white,
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
),
),
),
)),
const SizedBox(height: 24),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'لديك حساب بالفعل؟ '.tr,
style: const TextStyle(color: Colors.grey),
),
TextButton(
onPressed: () => Get.back(),
child: const Text(
'تسجيل الدخول',
style: TextStyle(
color: AppColor.blueColor,
fontWeight: FontWeight.bold,
),
),
),
],
),
const SizedBox(height: 20),
],
),
),
),
),
);
}
Widget _buildFieldTitle(String title) {
return Padding(
padding: const EdgeInsets.only(bottom: 8.0, right: 4.0),
child: Text(
title,
style: const TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
color: Colors.black54,
),
),
);
}
}

View File

@@ -0,0 +1,635 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:siro_service/constant/colors.dart';
import 'package:siro_service/constant/links.dart';
import 'package:siro_service/controller/functions/crud.dart';
import 'package:siro_service/controller/mainController/main_controller.dart';
import 'package:siro_service/controller/mainController/pages/best_driver_page.dart';
import 'package:siro_service/controller/mainController/pages/complaint.dart';
import 'package:siro_service/controller/mainController/pages/edit_car_plate.dart';
import 'package:siro_service/controller/mainController/pages/passengers_cant_regster.dart';
import 'package:siro_service/views/widgets/elevated_btn.dart';
import 'package:siro_service/views/widgets/my_dialog.dart';
import 'package:siro_service/views/widgets/my_textField.dart';
import '../../constant/style.dart';
import '../../controller/mainController/pages/add_car.dart';
import '../../controller/mainController/pages/drivers_cant_register.dart';
import '../../controller/mainController/pages/new_driver.dart';
import '../../controller/mainController/pages/welcome_call.dart';
import '../../main.dart';
import '../widgets/my_scafold.dart';
class Main extends StatelessWidget {
Main({super.key});
final MainController mainController = Get.put(MainController());
@override
Widget build(BuildContext context) {
return MyScaffold(title: 'Intaleq Service'.tr, isleading: false, body: [
Container(
color: AppColor.surfaceColor,
child: SingleChildScrollView(
physics: const BouncingScrollPhysics(),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// --- Header Section ---
_buildHeader(),
// --- Statistics Section ---
_buildStatsSection(),
// --- Actions Section ---
_buildCategoryTitle('🔍 Search & Inquiries'.tr),
_buildGridSection([
ServiceItem(
title: 'passenger details by phone'.tr,
icon: Icons.person_search_rounded,
color: Colors.blue,
onTap: () => _showSearchDialog(
title: 'insert passenger phone'.tr,
controller: mainController.passengerPhoneController,
onSearch: () => mainController.searchPassengerByPhone(),
),
),
ServiceItem(
title: 'Driver details by phone'.tr,
icon: Icons.contact_phone_rounded,
color: Colors.indigo,
onTap: () => _showSearchDialog(
title: 'insert Driver phone'.tr,
controller: mainController.driverPhoneController,
onSearch: () => mainController.searchDriverByPhone(),
),
),
ServiceItem(
title: 'Driver details by national number'.tr,
icon: Icons.badge_rounded,
color: Colors.teal,
onTap: () => _showSearchDialog(
title: 'insert Driver national'.tr,
controller: mainController.driverPhoneController,
type: TextInputType.number,
onSearch: () => mainController.searchDriverByNational(),
),
),
]),
_buildCategoryTitle('⏳ Approval Queue'.tr),
_buildGridSection([
ServiceItem(
title: 'Drivers waitting Register'.tr,
icon: Icons.pending_actions_rounded,
color: Colors.orange,
onTap: () async {
await mainController.getDriverWantCompleteRegistration();
Get.to(() => DriversCantRegister());
},
),
ServiceItem(
title: 'Drivers phones not register'.tr,
icon: Icons.phone_disabled_rounded,
color: Colors.blueGrey,
onTap: () async {
await mainController.getDriversPhoneNotComplete();
Get.to(() => DriversCantRegister());
},
),
ServiceItem(
title: 'Drivers Activity'.tr,
icon: Icons.assessment_rounded,
color: Colors.blueAccent,
onTap: () async {
await mainController.getDriversPhoneNotComplete();
Get.to(() => DriversCantRegister());
},
),
ServiceItem(
title: 'Drivers Cant Register'.tr,
icon: Icons.car_crash_rounded,
color: Colors.redAccent,
onTap: () async {
await mainController.getDriverNotCompleteRegistration();
Get.to(() => DriversCantRegister());
},
),
ServiceItem(
title: 'Passengers Cant Register'.tr,
icon: Icons.group_off_rounded,
color: Colors.deepOrange,
onTap: () async {
await mainController.getPassengerNotCompleteRegistration();
Get.to(() => PassengersCantRegister());
},
),
]),
_buildCategoryTitle('⚡ Quick Actions'.tr),
_buildGridSection([
ServiceItem(
title: 'Register new driver'.tr,
icon: Icons.person_add_rounded,
color: Colors.green,
onTap: () => Get.to(() => RegisterCaptain()),
),
ServiceItem(
title: 'Add Driver Who Wants to Work'.tr,
icon: Icons.handshake_rounded,
color: Colors.lightGreen,
onTap: () => _showAddDriverDialog(),
),
ServiceItem(
title: 'Add Car Who Wants to Work'.tr,
icon: Icons.add_road_rounded,
color: Colors.cyan,
onTap: () => _showAddCarDialog(),
),
]),
_buildCategoryTitle('🚗 Vehicle Management'.tr),
_buildGridSection([
ServiceItem(
title: 'Add car'.tr,
icon: Icons.add_circle_outline_rounded,
color: Colors.purple,
onTap: () async {
await mainController.getdriverWithoutCar();
if (mainController.driverWithoutCar.isNotEmpty) {
Get.to(() => const AddCar());
}
},
),
ServiceItem(
title: 'Edit car plate'.tr,
icon: Icons.edit_attributes_rounded,
color: Colors.amber,
onTap: () async {
await mainController.getCarPlateNotEdit();
if (mainController.carPlateNotEdit.isNotEmpty) {
Get.to(() => const EditCarPlate());
}
},
),
]),
_buildCategoryTitle('📊 Reporting & Quality'.tr),
_buildGridSection([
ServiceItem(
title: "View complaint".tr,
icon: Icons.report_problem_rounded,
color: Colors.deepOrange,
onTap: () => Get.to(() => const Complaint()),
),
ServiceItem(
title: "Welcome call".tr,
icon: Icons.ring_volume_rounded,
color: Colors.pink,
onTap: () async {
await mainController.getNewDriverRegister();
Get.to(() => const WelcomeCall());
},
),
ServiceItem(
title: "best driver".tr,
icon: Icons.emoji_events_rounded,
color: Colors.orangeAccent,
onTap: () async {
await mainController.getNewDriverRegister();
Get.to(() => DriverTheBest());
},
),
]),
const SizedBox(height: 32),
],
),
),
),
]);
}
Widget _buildHeader() {
return Container(
padding: const EdgeInsets.all(24),
decoration: const BoxDecoration(
color: AppColor.primaryColor,
borderRadius: BorderRadius.only(
bottomLeft: Radius.circular(32),
bottomRight: Radius.circular(32),
),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(height: 10),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Welcome back,'.tr,
style: AppStyle.title.copyWith(color: Colors.white70),
),
Text(
'Service Agent'.tr,
style: AppStyle.headTitle2.copyWith(color: Colors.white),
),
],
),
Row(
children: [
IconButton(
icon:
const Icon(Icons.refresh_rounded, color: Colors.white),
onPressed: () => mainController.refreshDashboardStats(),
),
const SizedBox(width: 8),
CircleAvatar(
radius: 24,
backgroundColor: Colors.white24,
child: IconButton(
icon: const Icon(Icons.logout, color: Colors.white),
onPressed: () {
box.erase();
Get.offAllNamed('/login');
},
),
),
],
),
],
),
const SizedBox(height: 24),
// --- Custom Search Bar ---
Container(
margin: const EdgeInsets.symmetric(horizontal: 16),
padding: const EdgeInsets.symmetric(horizontal: 16),
decoration: AppStyle.boxDecoration.copyWith(
color: Colors.white.withValues(alpha: 0.95),
borderRadius: BorderRadius.circular(16),
),
child: TextField(
decoration: InputDecoration(
hintText: 'Quick Search...'.tr,
border: InputBorder.none,
icon: const Icon(Icons.search, color: AppColor.primaryColor),
),
onSubmitted: (val) {
// Global search logic
},
),
),
],
),
);
}
Widget _buildStatsSection() {
return GetBuilder<MainController>(
builder: (controller) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 20.0, horizontal: 16.0),
child: Row(
children: [
_buildStatCard(
'Pending'.tr,
controller.isLoading
? '...'
: controller.driverWantCompleteRegistration.length
.toString(),
Icons.timer_rounded,
Colors.orange,
),
const SizedBox(width: 12),
_buildStatCard(
'New'.tr,
controller.isLoading
? '...'
: controller.newDriverRegister.length.toString(),
Icons.person_add_alt_1_rounded,
Colors.green,
),
const SizedBox(width: 12),
_buildStatCard(
'Issues'.tr,
controller.isLoading
? '...'
: controller.driverNotCompleteRegistration.length
.toString(),
Icons.warning_rounded,
Colors.red,
),
],
),
);
},
);
}
Widget _buildStatCard(
String title, String count, IconData icon, Color color) {
return Expanded(
child: Container(
padding: const EdgeInsets.all(16),
decoration: AppStyle.boxDecoration,
child: Column(
children: [
Icon(icon, color: color, size: 24),
const SizedBox(height: 8),
Text(count, style: AppStyle.headTitle2.copyWith(fontSize: 20)),
Text(title,
style: AppStyle.subtitle.copyWith(color: AppColor.accentColor)),
],
),
),
);
}
Widget _buildCategoryTitle(String title) {
return Padding(
padding: const EdgeInsets.fromLTRB(20, 24, 20, 12),
child: Text(
title,
style: AppStyle.headTitle2.copyWith(fontSize: 18),
),
);
}
Widget _buildGridSection(List<ServiceItem> items) {
return GridView.builder(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
padding: const EdgeInsets.symmetric(horizontal: 16),
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
crossAxisSpacing: 12,
mainAxisSpacing: 12,
childAspectRatio: 1.15,
),
itemCount: items.length,
itemBuilder: (context, index) {
final item = items[index];
return _buildServiceCard(item);
},
);
}
Widget _buildServiceCard(ServiceItem item) {
return InkWell(
onTap: item.onTap,
borderRadius: BorderRadius.circular(20),
child: Container(
decoration: AppStyle.boxDecoration1,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: item.color.withValues(alpha: 0.1),
shape: BoxShape.circle,
),
child: Icon(item.icon, color: item.color, size: 30),
),
const SizedBox(height: 12),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0),
child: Text(
item.title,
textAlign: TextAlign.center,
style: AppStyle.title
.copyWith(fontSize: 13, fontWeight: FontWeight.w600),
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
),
],
),
),
);
}
void _showSearchDialog({
required String title,
required TextEditingController controller,
required VoidCallback onSearch,
TextInputType type = TextInputType.phone,
}) {
MyDialog().getDialog(
title,
'Search Details'.tr,
Form(
key: mainController.formKey,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
MyTextForm(
controller: controller,
label: title,
hint: title,
type: type,
validator: (val) {
if (val == null || val.isEmpty) {
return 'Please enter a value'.tr;
}
return null;
},
),
],
),
),
onSearch,
);
}
void _showAddDriverDialog() {
Get.defaultDialog(
barrierDismissible: false,
title: "Add Driver Who Wants to Work".tr,
titleStyle: AppStyle.headTitle2,
content: SizedBox(
width: Get.width * .8,
height: 350,
child: Form(
key: mainController.formKey,
child: ListView(
padding: const EdgeInsets.all(8),
children: [
MyTextForm(
controller: mainController.driverNameController,
label: 'Insert Name of Driver'.tr,
hint: 'Insert Name of Driver'.tr,
type: TextInputType.name),
MyTextForm(
controller: mainController.nationalIdController,
label: 'Insert national ID of Driver'.tr,
hint: 'Insert national ID of Driver'.tr,
type: TextInputType.number),
MyTextForm(
controller: mainController.phoneController,
label: 'Insert phone of Driver'.tr,
hint: 'Insert phone of Driver'.tr,
type: TextInputType.phone),
MyTextForm(
controller: mainController.licenseTypeController,
label: 'Insert license type of Driver'.tr,
hint: 'Insert license type of Driver'.tr,
type: TextInputType.name),
MyTextForm(
controller: mainController.siteDriverController,
label: 'Insert site of Driver'.tr,
hint: 'Insert site of Driver'.tr,
type: TextInputType.name),
MyTextForm(
controller: mainController.birthDateController,
label: 'Insert birth_date of Driver'.tr,
hint: 'YYYY-MM-DD'.tr,
type: TextInputType.datetime),
],
),
),
),
confirm: MyElevatedButton(
title: 'Add'.tr,
onPressed: () async {
var res =
await CRUD().post(link: AppLink.addDriverWantWork, payload: {
"driver_name": mainController.driverNameController.text,
"national_id": mainController.nationalIdController.text,
"birth_date": mainController.birthDateController.text,
"site": mainController.siteDriverController.text,
"license_type": mainController.licenseTypeController.text,
"phone": mainController.phoneController.text,
});
if (res != 'failure' && res['status'] == 'success') {
Get.back();
mainController.driverNameController.clear();
mainController.nationalIdController.clear();
mainController.birthDateController.clear();
mainController.licenseTypeController.clear();
mainController.siteDriverController.clear();
mainController.phoneController.clear();
Get.snackbar('Success'.tr, 'Added successfully'.tr,
backgroundColor: AppColor.greenColor, colorText: Colors.white);
}
},
kolor: AppColor.greenColor,
),
cancel: MyElevatedButton(
title: 'Cancel'.tr,
kolor: AppColor.redColor,
onPressed: () => Get.back()),
);
}
void _showAddCarDialog() {
Get.defaultDialog(
barrierDismissible: false,
title: "Add Car Who Wants to Work".tr,
titleStyle: AppStyle.headTitle2,
content: SizedBox(
width: Get.width * .8,
height: 350,
child: Form(
key: mainController.formKey,
child: ListView(
padding: const EdgeInsets.all(8),
children: [
MyTextForm(
controller: mainController.carOwnerWorkController,
label: 'Insert Name of Owner'.tr,
hint: 'Insert Name of Owner'.tr,
type: TextInputType.name),
MyTextForm(
controller: mainController.carNumberController,
label: 'Insert car_number of Driver'.tr,
hint: 'Insert car_number of Driver'.tr,
type: TextInputType.name),
MyTextForm(
controller: mainController.phoneCarController,
label: 'Insert phone of Owner'.tr,
hint: 'Insert phone of Owner'.tr,
type: TextInputType.phone),
MyTextForm(
controller: mainController.manufactureYearController,
label: 'Insert year of Car'.tr,
hint: 'Insert year of Car'.tr,
type: TextInputType.number),
MyTextForm(
controller: mainController.carModelController,
label: 'Insert car_model of Driver'.tr,
hint: 'Insert car_model of Driver'.tr,
type: TextInputType.name),
MyTextForm(
controller: mainController.siteCarController,
label: 'Insert site of Owner'.tr,
hint: 'Insert site of Owner'.tr,
type: TextInputType.name),
MyTextForm(
controller: mainController.carTypeController,
label: 'Insert car_type of Driver'.tr,
hint: 'Insert car_type of Driver'.tr,
type: TextInputType.name),
MyTextForm(
controller: mainController.registrationDateController,
label: 'Insert registration_date of Car'.tr,
hint: 'YYYY-MM-DD'.tr,
type: TextInputType.datetime),
],
),
),
),
confirm: MyElevatedButton(
title: 'Add'.tr,
onPressed: () async {
var res = await CRUD().post(link: AppLink.addCarWantWork, payload: {
"owner_name": mainController.carOwnerWorkController.text,
"car_number": mainController.carNumberController.text,
"manufacture_year": mainController.manufactureYearController.text,
"car_model": mainController.carModelController.text,
"car_type": mainController.carTypeController.text,
"site": mainController.siteCarController.text,
"registration_date": mainController.registrationDateController.text,
"phone": mainController.phoneCarController.text,
});
if (res != 'failure' && res['status'] == 'success') {
Get.back();
mainController.carOwnerWorkController.clear();
mainController.carNumberController.clear();
mainController.manufactureYearController.clear();
mainController.carModelController.clear();
mainController.siteCarController.clear();
mainController.carTypeController.clear();
mainController.registrationDateController.clear();
mainController.phoneCarController.clear();
Get.snackbar('Success'.tr, 'Added successfully'.tr,
backgroundColor: AppColor.greenColor, colorText: Colors.white);
}
},
kolor: AppColor.greenColor,
),
cancel: MyElevatedButton(
title: 'Cancel'.tr,
kolor: AppColor.redColor,
onPressed: () => Get.back()),
);
}
}
class ServiceItem {
final String title;
final IconData icon;
final Color color;
final VoidCallback onTap;
ServiceItem({
required this.title,
required this.icon,
required this.color,
required this.onTap,
});
}

View File

@@ -0,0 +1,63 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../../constant/colors.dart';
class MyCircleContainer extends StatelessWidget {
final Widget child;
final Color backgroundColor;
final Color borderColor;
MyCircleContainer({
Key? key,
required this.child,
this.backgroundColor = AppColor.secondaryColor,
this.borderColor = AppColor.accentColor,
}) : super(key: key);
final controller = Get.put(CircleController());
@override
Widget build(BuildContext context) {
return GetBuilder<CircleController>(
builder: ((controller) => GestureDetector(
onTap: () {
controller.changeColor();
},
child: AnimatedContainer(
onEnd: () {
controller.onEnd();
},
duration: const Duration(milliseconds: 300),
width: controller.size,
height: controller.size,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: controller.backgroundColor,
border: Border.all(
color: borderColor,
width: 1,
),
),
child: Center(child: child),
),
)));
}
}
class CircleController extends GetxController {
Color backgroundColor = AppColor.secondaryColor;
double size = 40;
void changeColor() {
backgroundColor = backgroundColor == AppColor.secondaryColor
? AppColor.accentColor
: AppColor.secondaryColor;
size = 60;
update();
}
void onEnd() {
size = 40;
update();
}
}

View File

@@ -0,0 +1,69 @@
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:vibration/vibration.dart';
import '../../constant/box_name.dart';
import '../../constant/colors.dart';
import '../../constant/style.dart';
import '../../main.dart';
class MyElevatedButton extends StatelessWidget {
final String title;
final VoidCallback onPressed;
final Color kolor;
final int vibrateDuration;
final bool loading;
final Widget? child;
const MyElevatedButton({
Key? key,
required this.title,
required this.onPressed,
this.kolor = AppColor.primaryColor,
this.vibrateDuration = 100,
this.loading = false,
this.child,
}) : super(key: key);
@override
Widget build(BuildContext context) {
bool vibrate = box.read(BoxName.isvibrate) ?? true;
return ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: kolor,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12.0),
),
),
onPressed: loading
? null
: () async {
if (vibrate == true) {
if (Platform.isIOS) {
HapticFeedback.selectionClick();
} else if (Platform.isAndroid) {
await Vibration.vibrate(duration: vibrateDuration);
} else {}
}
onPressed();
},
child: loading
? const SizedBox(
height: 20,
width: 20,
child: CircularProgressIndicator(
color: Colors.white,
strokeWidth: 2,
),
)
: child ??
Text(
title,
textAlign: TextAlign.center,
style: AppStyle.title.copyWith(color: AppColor.secondaryColor),
),
);
}
}

View File

@@ -0,0 +1,66 @@
import 'package:flutter/material.dart';
import '../../constant/colors.dart';
import '../../constant/style.dart';
class IconWidgetMenu extends StatelessWidget {
const IconWidgetMenu({
Key? key,
required this.onpressed,
required this.icon,
required this.title,
}) : super(key: key);
final VoidCallback onpressed;
final IconData icon;
final String title;
@override
Widget build(BuildContext context) {
return InkWell(
onTap: onpressed,
child: Padding(
padding: const EdgeInsets.only(top: 1),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
width: 50,
decoration: const BoxDecoration(
color: AppColor.secondaryColor,
shape: BoxShape.circle,
// boxShadow: [
// BoxShadow(
// color: AppColor.secondaryColor,
// offset: Offset(-2, -2),
// blurRadius: 0,
// spreadRadius: 0,
// blurStyle: BlurStyle.outer,
// ),
// BoxShadow(
// color: AppColor.accentColor,
// offset: Offset(3, 3),
// blurRadius: 0,
// spreadRadius: 0,
// blurStyle: BlurStyle.outer,
// ),
// ],
),
child: Center(
child: Icon(
icon,
size: 30,
color: AppColor.primaryColor,
),
),
),
Text(
title,
style: AppStyle.subtitle.copyWith(color: AppColor.secondaryColor),
)
],
),
),
);
}
}

View File

@@ -0,0 +1,40 @@
import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../../constant/colors.dart';
import '../../constant/style.dart';
import 'elevated_btn.dart';
class MyDialog extends GetxController {
void getDialog(
String title, String? midTitle, Widget widget, VoidCallback onPressed) {
// final textToSpeechController = Get.put(TextToSpeechController());
Get.defaultDialog(
title: title,
titleStyle: AppStyle.title,
middleTextStyle: AppStyle.title,
content: Column(
children: [
// IconButton(
// onPressed: () async {
// // await textToSpeechController.speakText(title ?? midTitle!);
// },
// icon: const Icon(Icons.headphones)),
widget
],
),
confirm: MyElevatedButton(
title: 'Ok'.tr,
onPressed: onPressed,
kolor: AppColor.greenColor,
),
cancel: MyElevatedButton(
title: 'Cancel',
kolor: AppColor.redColor,
onPressed: () {
Get.back();
}));
}
}

View File

@@ -0,0 +1,51 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../../constant/colors.dart';
import '../../constant/style.dart';
class MyScaffold extends StatelessWidget {
const MyScaffold({
super.key,
required this.title,
this.action = const Icon(
Icons.clear,
color: AppColor.secondaryColor,
),
required this.isleading,
required this.body,
});
final String title;
final List<Widget> body;
final Widget action;
final bool isleading;
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: AppColor.secondaryColor,
appBar: AppBar(
backgroundColor: AppColor.secondaryColor,
elevation: 0,
leading: isleading
? IconButton(
onPressed: () {
Get.back();
},
icon: const Icon(
Icons.arrow_back_ios_new,
color: AppColor.primaryColor,
),
)
: const SizedBox(),
actions: [action],
title: Text(
title,
style: AppStyle.title
.copyWith(fontSize: 20, fontWeight: FontWeight.bold),
),
),
body: SafeArea(child: Stack(children: body)));
}
}

View File

@@ -0,0 +1,69 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../../constant/colors.dart';
import '../../constant/style.dart';
class MyTextForm extends StatelessWidget {
const MyTextForm({
super.key,
required this.controller,
required this.label,
required this.hint,
required this.type,
this.validator,
});
final TextEditingController controller;
final String label, hint;
final TextInputType type;
final String? Function(String?)? validator;
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.only(bottom: 10),
child: SizedBox(
width: Get.width * .8,
child: TextFormField(
keyboardType: type,
cursorColor: AppColor.accentColor,
controller: controller,
decoration: InputDecoration(
focusedBorder: OutlineInputBorder(
borderSide: const BorderSide(
color: AppColor.primaryColor,
width: 2.0,
),
borderRadius: BorderRadius.circular(10),
),
focusColor: AppColor.accentColor,
fillColor: AppColor.accentColor,
border: const OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(12))),
labelText: label.tr,
hintText: hint.tr,
hintStyle: AppStyle.title,
labelStyle: AppStyle.title,
),
validator: validator ?? (value) {
if (value == null || value.isEmpty) {
return '${'Please enter'.tr} $label.'.tr;
}
if (type == TextInputType.emailAddress) {
if (!value.contains('@')) {
return 'Please enter a valid email.'.tr;
}
} else if (type == TextInputType.phone) {
if (value.length > 14) {
return 'Please enter a valid phone number.'.tr;
}
}
return null;
},
),
),
);
}
}

View File

@@ -0,0 +1,160 @@
import 'package:flutter/material.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:get/get.dart';
import '../../constant/colors.dart';
class MyCircularProgressIndicator extends StatelessWidget {
final Color backgroundColor;
const MyCircularProgressIndicator({
super.key,
this.backgroundColor = Colors.transparent,
});
@override
Widget build(BuildContext context) {
return Center(
child: Container(
width: 110,
height: 110,
decoration: BoxDecoration(
color: backgroundColor,
shape: BoxShape.circle,
),
child: Stack(
children: [
const Center(child: CircularProgressIndicator()),
Column(
children: [
Align(
alignment: Alignment.center,
child: Image.asset('assets/images/logo.png'),
),
],
),
],
),
),
);
}
}
class SnackbarConfig {
static const duration = Duration(seconds: 3);
static const animationDuration = Duration(milliseconds: 300);
static const margin = EdgeInsets.symmetric(horizontal: 16, vertical: 10);
static const borderRadius = 12.0;
static const elevation = 6.0;
static final BoxShadow shadow = BoxShadow(
color: Colors.black.withOpacity(0.1),
blurRadius: 10,
offset: const Offset(0, 2),
);
}
SnackbarController mySnackeBarError(String message) {
// Trigger error haptic feedback
HapticFeedback.mediumImpact();
return Get.snackbar(
'Error'.tr,
message,
backgroundColor: AppColor.redColor.withOpacity(0.95),
colorText: AppColor.secondaryColor,
icon: const Icon(
Icons.error_outline_rounded,
color: AppColor.secondaryColor,
size: 28,
),
shouldIconPulse: true,
snackPosition: SnackPosition.TOP,
margin: SnackbarConfig.margin,
borderRadius: SnackbarConfig.borderRadius,
duration: SnackbarConfig.duration,
animationDuration: SnackbarConfig.animationDuration,
forwardAnimationCurve: Curves.easeOutCirc,
reverseAnimationCurve: Curves.easeInCirc,
boxShadows: [SnackbarConfig.shadow],
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 12),
titleText: Text(
'Error'.tr,
style: const TextStyle(
fontWeight: FontWeight.w700,
color: Colors.white,
fontSize: 16,
letterSpacing: 0.2,
),
),
messageText: Text(
message,
style: TextStyle(
color: Colors.white.withOpacity(0.95),
fontSize: 14,
height: 1.3,
),
),
onTap: (_) {
HapticFeedback.lightImpact();
Get.closeCurrentSnackbar();
},
isDismissible: true,
dismissDirection: DismissDirection.horizontal,
overlayBlur: 0.8,
overlayColor: Colors.black12,
);
}
SnackbarController mySnackbarSuccess(String message) {
// Trigger success haptic feedback
HapticFeedback.lightImpact();
return Get.snackbar(
'Success'.tr,
message,
backgroundColor: AppColor.greenColor.withOpacity(0.95),
colorText: AppColor.secondaryColor,
icon: const Icon(
Icons.check_circle_outline_rounded,
color: AppColor.secondaryColor,
size: 28,
),
shouldIconPulse: true,
snackPosition: SnackPosition.TOP,
margin: SnackbarConfig.margin,
borderRadius: SnackbarConfig.borderRadius,
duration: SnackbarConfig.duration,
animationDuration: SnackbarConfig.animationDuration,
forwardAnimationCurve: Curves.easeOutCirc,
reverseAnimationCurve: Curves.easeInCirc,
boxShadows: [SnackbarConfig.shadow],
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 12),
titleText: Text(
'Success'.tr,
style: const TextStyle(
fontWeight: FontWeight.w700,
color: Colors.white,
fontSize: 16,
letterSpacing: 0.2,
),
),
messageText: Text(
message,
style: TextStyle(
color: Colors.white.withOpacity(0.95),
fontSize: 14,
height: 1.3,
),
),
onTap: (_) {
HapticFeedback.lightImpact();
Get.closeCurrentSnackbar();
},
isDismissible: true,
dismissDirection: DismissDirection.horizontal,
overlayBlur: 0.8,
overlayColor: Colors.black12,
);
}