first commit
This commit is contained in:
27
siro_service/lib/constant/api_key.dart
Normal file
27
siro_service/lib/constant/api_key.dart
Normal 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);
|
||||
}
|
||||
98
siro_service/lib/constant/box_name.dart
Normal file
98
siro_service/lib/constant/box_name.dart
Normal 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';
|
||||
}
|
||||
68
siro_service/lib/constant/char_map.dart
Normal file
68
siro_service/lib/constant/char_map.dart
Normal 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"
|
||||
};
|
||||
25
siro_service/lib/constant/colors.dart
Normal file
25
siro_service/lib/constant/colors.dart
Normal 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);
|
||||
}
|
||||
|
||||
141
siro_service/lib/constant/credential.dart
Normal file
141
siro_service/lib/constant/credential.dart
Normal 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;
|
||||
}
|
||||
}
|
||||
4471
siro_service/lib/constant/info.dart
Normal file
4471
siro_service/lib/constant/info.dart
Normal file
File diff suppressed because one or more lines are too long
105
siro_service/lib/constant/links.dart
Normal file
105
siro_service/lib/constant/links.dart
Normal 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";
|
||||
}
|
||||
65
siro_service/lib/constant/style.dart
Normal file
65
siro_service/lib/constant/style.dart
Normal 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),
|
||||
);
|
||||
|
||||
|
||||
}
|
||||
9
siro_service/lib/constant/table_names.dart
Normal file
9
siro_service/lib/constant/table_names.dart
Normal 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";
|
||||
}
|
||||
78
siro_service/lib/controller/auth/register_controller.dart
Normal file
78
siro_service/lib/controller/auth/register_controller.dart
Normal 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();
|
||||
}
|
||||
}
|
||||
71
siro_service/lib/controller/best_driver_controllers.dart
Normal file
71
siro_service/lib/controller/best_driver_controllers.dart
Normal 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();
|
||||
}
|
||||
}
|
||||
110
siro_service/lib/controller/firbase_messge.dart
Normal file
110
siro_service/lib/controller/firbase_messge.dart
Normal 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');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
109
siro_service/lib/controller/firebase.dart
Normal file
109
siro_service/lib/controller/firebase.dart
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
386
siro_service/lib/controller/functions/crud.dart
Normal file
386
siro_service/lib/controller/functions/crud.dart
Normal 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) {}
|
||||
}
|
||||
}
|
||||
49
siro_service/lib/controller/functions/device_helper.dart
Normal file
49
siro_service/lib/controller/functions/device_helper.dart
Normal 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 '';
|
||||
}
|
||||
}
|
||||
}
|
||||
87
siro_service/lib/controller/functions/encrypt_decrypt.dart
Normal file
87
siro_service/lib/controller/functions/encrypt_decrypt.dart
Normal 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();
|
||||
}
|
||||
340
siro_service/lib/controller/functions/image.dart
Normal file
340
siro_service/lib/controller/functions/image.dart
Normal 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);
|
||||
}
|
||||
43
siro_service/lib/controller/functions/initilize.dart
Normal file
43
siro_service/lib/controller/functions/initilize.dart
Normal 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 {}
|
||||
}
|
||||
}
|
||||
}
|
||||
76
siro_service/lib/controller/functions/launch.dart
Normal file
76
siro_service/lib/controller/functions/launch.dart
Normal 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 {}
|
||||
}
|
||||
32
siro_service/lib/controller/functions/security_helper.dart
Normal file
32
siro_service/lib/controller/functions/security_helper.dart
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
102
siro_service/lib/controller/local/local_controller.dart
Normal file
102
siro_service/lib/controller/local/local_controller.dart
Normal 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();
|
||||
}
|
||||
}
|
||||
9187
siro_service/lib/controller/local/translations.dart
Normal file
9187
siro_service/lib/controller/local/translations.dart
Normal file
File diff suppressed because it is too large
Load Diff
330
siro_service/lib/controller/local_notification.dart
Normal file
330
siro_service/lib/controller/local_notification.dart
Normal 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');
|
||||
}
|
||||
}
|
||||
89
siro_service/lib/controller/login_controller.dart
Normal file
89
siro_service/lib/controller/login_controller.dart
Normal 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();
|
||||
}
|
||||
}
|
||||
567
siro_service/lib/controller/mainController/main_controller.dart
Normal file
567
siro_service/lib/controller/mainController/main_controller.dart
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
378
siro_service/lib/controller/mainController/pages/add_car.dart
Normal file
378
siro_service/lib/controller/mainController/pages/add_car.dart
Normal 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,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
],
|
||||
)
|
||||
]);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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: []);
|
||||
}
|
||||
}
|
||||
13071
siro_service/lib/controller/mainController/pages/contact_page.dart
Normal file
13071
siro_service/lib/controller/mainController/pages/contact_page.dart
Normal file
File diff suppressed because it is too large
Load Diff
@@ -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، معك الدعم الفني من شركة انطلق. كيف يمكنني مساعدتك اليوم؟',
|
||||
),
|
||||
),
|
||||
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
334
siro_service/lib/controller/mainController/pages/edit_car.dart
Normal file
334
siro_service/lib/controller/mainController/pages/edit_car.dart
Normal 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,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
],
|
||||
)
|
||||
]);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -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']))),
|
||||
),
|
||||
);
|
||||
}),
|
||||
),
|
||||
],
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
);
|
||||
}
|
||||
}
|
||||
485
siro_service/lib/controller/mainController/pages/new_driver.dart
Normal file
485
siro_service/lib/controller/mainController/pages/new_driver.dart
Normal 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),
|
||||
),
|
||||
);
|
||||
}),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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');
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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)),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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),
|
||||
),
|
||||
);
|
||||
}),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
147
siro_service/lib/controller/themes/themes.dart
Normal file
147
siro_service/lib/controller/themes/themes.dart
Normal 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
219
siro_service/lib/env/env.dart
vendored
Normal 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
2137
siro_service/lib/env/env.g.dart
vendored
Normal file
File diff suppressed because it is too large
Load Diff
67
siro_service/lib/firebase_options.dart
Normal file
67
siro_service/lib/firebase_options.dart
Normal 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',
|
||||
);
|
||||
}
|
||||
172
siro_service/lib/login_page.dart
Normal file
172
siro_service/lib/login_page.dart
Normal 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,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
49
siro_service/lib/main.dart
Normal file
49
siro_service/lib/main.dart
Normal 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(),
|
||||
);
|
||||
}
|
||||
}
|
||||
115
siro_service/lib/models/cloudpanel.md
Normal file
115
siro_service/lib/models/cloudpanel.md
Normal 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
208
siro_service/lib/models/sql
Normal 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;
|
||||
99
siro_service/lib/models/vshost.md
Normal file
99
siro_service/lib/models/vshost.md
Normal 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
|
||||
13
siro_service/lib/print.dart
Normal file
13
siro_service/lib/print.dart
Normal 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);
|
||||
}
|
||||
}
|
||||
222
siro_service/lib/translations_ar.json
Normal file
222
siro_service/lib/translations_ar.json
Normal 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 Driver’s 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 Driver’s 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": ""
|
||||
}
|
||||
222
siro_service/lib/translations_en.json
Normal file
222
siro_service/lib/translations_en.json
Normal 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 Driver’s 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 Driver’s 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": ""
|
||||
}
|
||||
172
siro_service/lib/views/auth/register_page.dart
Normal file
172
siro_service/lib/views/auth/register_page.dart
Normal 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,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
635
siro_service/lib/views/home/main.dart
Normal file
635
siro_service/lib/views/home/main.dart
Normal 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,
|
||||
});
|
||||
}
|
||||
63
siro_service/lib/views/widgets/circle_container.dart
Normal file
63
siro_service/lib/views/widgets/circle_container.dart
Normal 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();
|
||||
}
|
||||
}
|
||||
69
siro_service/lib/views/widgets/elevated_btn.dart
Normal file
69
siro_service/lib/views/widgets/elevated_btn.dart
Normal 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),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
66
siro_service/lib/views/widgets/icon_widget_menu.dart
Normal file
66
siro_service/lib/views/widgets/icon_widget_menu.dart
Normal 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),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
40
siro_service/lib/views/widgets/my_dialog.dart
Normal file
40
siro_service/lib/views/widgets/my_dialog.dart
Normal 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();
|
||||
}));
|
||||
}
|
||||
}
|
||||
51
siro_service/lib/views/widgets/my_scafold.dart
Normal file
51
siro_service/lib/views/widgets/my_scafold.dart
Normal 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)));
|
||||
}
|
||||
}
|
||||
69
siro_service/lib/views/widgets/my_textField.dart
Normal file
69
siro_service/lib/views/widgets/my_textField.dart
Normal 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;
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
160
siro_service/lib/views/widgets/mycircular.dart
Normal file
160
siro_service/lib/views/widgets/mycircular.dart
Normal 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,
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user