diff --git a/android/app/build.gradle.kts b/android/app/build.gradle.kts index 4c04ec4..3d6d637 100644 --- a/android/app/build.gradle.kts +++ b/android/app/build.gradle.kts @@ -28,7 +28,7 @@ if (keystorePropertiesFile.exists()) { android { namespace = "com.service_intaleq" compileSdk = 36 - ndkVersion = "27.0.12077973" + // ndkVersion = "27.0.12077973" defaultConfig { applicationId = "com.service_intaleq" @@ -39,7 +39,7 @@ android { multiDexEnabled = true ndk { - abiFilters += listOf("armeabi-v7a", "arm64-v8a", "x86", "x86_64") + abiFilters += listOf("armeabi-v7a", "arm64-v8a") } } @@ -67,6 +67,7 @@ android { compileOptions { sourceCompatibility = JavaVersion.VERSION_11 targetCompatibility = JavaVersion.VERSION_11 + isCoreLibraryDesugaringEnabled = true } kotlinOptions { @@ -76,7 +77,7 @@ android { externalNativeBuild { cmake { path = file("src/main/cpp/CMakeLists.txt") - version = "3.31.5" + version = "3.22.1" } } } diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index a7f3359..848f5bc 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -1,54 +1,97 @@ + + - + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + android:windowSoftInputMode="adjustResize" + android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"> + + + android:resource="@style/NormalTheme" /> + + - + + - - - - - - - \ No newline at end of file diff --git a/ios/Podfile b/ios/Podfile index e549ee2..6649374 100644 --- a/ios/Podfile +++ b/ios/Podfile @@ -1,5 +1,5 @@ # Uncomment this line to define a global platform for your project -# platform :ios, '12.0' +platform :ios, '15.0' # CocoaPods analytics sends network stats synchronously affecting flutter build latency. ENV['COCOAPODS_DISABLE_STATS'] = 'true' diff --git a/ios/Podfile.lock b/ios/Podfile.lock new file mode 100644 index 0000000..d080027 --- /dev/null +++ b/ios/Podfile.lock @@ -0,0 +1,297 @@ +PODS: + - AppAuth (2.0.0): + - AppAuth/Core (= 2.0.0) + - AppAuth/ExternalUserAgent (= 2.0.0) + - AppAuth/Core (2.0.0) + - AppAuth/ExternalUserAgent (2.0.0): + - AppAuth/Core + - AppCheckCore (11.2.0): + - GoogleUtilities/Environment (~> 8.0) + - GoogleUtilities/UserDefaults (~> 8.0) + - PromisesObjC (~> 2.4) + - device_info_plus (0.0.1): + - Flutter + - Firebase/Auth (12.0.0): + - Firebase/CoreOnly + - FirebaseAuth (~> 12.0.0) + - Firebase/CoreOnly (12.0.0): + - FirebaseCore (~> 12.0.0) + - Firebase/Messaging (12.0.0): + - Firebase/CoreOnly + - FirebaseMessaging (~> 12.0.0) + - firebase_auth (6.0.0): + - Firebase/Auth (= 12.0.0) + - firebase_core + - Flutter + - firebase_core (4.0.0): + - Firebase/CoreOnly (= 12.0.0) + - Flutter + - firebase_messaging (16.0.0): + - Firebase/Messaging (= 12.0.0) + - firebase_core + - Flutter + - FirebaseAppCheckInterop (12.0.0) + - FirebaseAuth (12.0.0): + - FirebaseAppCheckInterop (~> 12.0.0) + - FirebaseAuthInterop (~> 12.0.0) + - FirebaseCore (~> 12.0.0) + - FirebaseCoreExtension (~> 12.0.0) + - GoogleUtilities/AppDelegateSwizzler (~> 8.1) + - GoogleUtilities/Environment (~> 8.1) + - GTMSessionFetcher/Core (< 6.0, >= 3.4) + - RecaptchaInterop (~> 101.0) + - FirebaseAuthInterop (12.0.0) + - FirebaseCore (12.0.0): + - FirebaseCoreInternal (~> 12.0.0) + - GoogleUtilities/Environment (~> 8.1) + - GoogleUtilities/Logger (~> 8.1) + - FirebaseCoreExtension (12.0.0): + - FirebaseCore (~> 12.0.0) + - FirebaseCoreInternal (12.0.0): + - "GoogleUtilities/NSData+zlib (~> 8.1)" + - FirebaseInstallations (12.0.0): + - FirebaseCore (~> 12.0.0) + - GoogleUtilities/Environment (~> 8.1) + - GoogleUtilities/UserDefaults (~> 8.1) + - PromisesObjC (~> 2.4) + - FirebaseMessaging (12.0.0): + - FirebaseCore (~> 12.0.0) + - FirebaseInstallations (~> 12.0.0) + - GoogleDataTransport (~> 10.1) + - GoogleUtilities/AppDelegateSwizzler (~> 8.1) + - GoogleUtilities/Environment (~> 8.1) + - GoogleUtilities/Reachability (~> 8.1) + - GoogleUtilities/UserDefaults (~> 8.1) + - nanopb (~> 3.30910.0) + - Flutter (1.0.0) + - flutter_image_compress_common (1.0.0): + - Flutter + - Mantle + - SDWebImage + - SDWebImageWebPCoder + - flutter_local_notifications (0.0.1): + - Flutter + - flutter_secure_storage (6.0.0): + - Flutter + - google_sign_in_ios (0.0.1): + - Flutter + - FlutterMacOS + - GoogleSignIn (~> 9.0) + - GTMSessionFetcher (>= 3.4.0) + - GoogleDataTransport (10.1.0): + - nanopb (~> 3.30910.0) + - PromisesObjC (~> 2.4) + - GoogleSignIn (9.0.0): + - AppAuth (~> 2.0) + - AppCheckCore (~> 11.0) + - GTMAppAuth (~> 5.0) + - GTMSessionFetcher/Core (~> 3.3) + - GoogleUtilities/AppDelegateSwizzler (8.1.0): + - GoogleUtilities/Environment + - GoogleUtilities/Logger + - GoogleUtilities/Network + - GoogleUtilities/Privacy + - GoogleUtilities/Environment (8.1.0): + - GoogleUtilities/Privacy + - GoogleUtilities/Logger (8.1.0): + - GoogleUtilities/Environment + - GoogleUtilities/Privacy + - GoogleUtilities/Network (8.1.0): + - GoogleUtilities/Logger + - "GoogleUtilities/NSData+zlib" + - GoogleUtilities/Privacy + - GoogleUtilities/Reachability + - "GoogleUtilities/NSData+zlib (8.1.0)": + - GoogleUtilities/Privacy + - GoogleUtilities/Privacy (8.1.0) + - GoogleUtilities/Reachability (8.1.0): + - GoogleUtilities/Logger + - GoogleUtilities/Privacy + - GoogleUtilities/UserDefaults (8.1.0): + - GoogleUtilities/Logger + - GoogleUtilities/Privacy + - GTMAppAuth (5.0.0): + - AppAuth/Core (~> 2.0) + - GTMSessionFetcher/Core (< 4.0, >= 3.3) + - GTMSessionFetcher (3.5.0): + - GTMSessionFetcher/Full (= 3.5.0) + - GTMSessionFetcher/Core (3.5.0) + - GTMSessionFetcher/Full (3.5.0): + - GTMSessionFetcher/Core + - image_cropper (0.0.4): + - Flutter + - TOCropViewController (~> 2.7.4) + - image_picker_ios (0.0.1): + - Flutter + - libwebp (1.5.0): + - libwebp/demux (= 1.5.0) + - libwebp/mux (= 1.5.0) + - libwebp/sharpyuv (= 1.5.0) + - libwebp/webp (= 1.5.0) + - libwebp/demux (1.5.0): + - libwebp/webp + - libwebp/mux (1.5.0): + - libwebp/demux + - libwebp/sharpyuv (1.5.0) + - libwebp/webp (1.5.0): + - libwebp/sharpyuv + - Mantle (2.2.0): + - Mantle/extobjc (= 2.2.0) + - Mantle/extobjc (2.2.0) + - nanopb (3.30910.0): + - nanopb/decode (= 3.30910.0) + - nanopb/encode (= 3.30910.0) + - nanopb/decode (3.30910.0) + - nanopb/encode (3.30910.0) + - path_provider_foundation (0.0.1): + - Flutter + - FlutterMacOS + - permission_handler_apple (9.3.0): + - Flutter + - PromisesObjC (2.4.0) + - RecaptchaInterop (101.0.0) + - SDWebImage (5.21.2): + - SDWebImage/Core (= 5.21.2) + - SDWebImage/Core (5.21.2) + - SDWebImageWebPCoder (0.14.6): + - libwebp (~> 1.0) + - SDWebImage/Core (~> 5.17) + - share_plus (0.0.1): + - Flutter + - sqflite_darwin (0.0.4): + - Flutter + - FlutterMacOS + - TOCropViewController (2.7.4) + - url_launcher_ios (0.0.1): + - Flutter + - vibration (3.0.0): + - Flutter + +DEPENDENCIES: + - device_info_plus (from `.symlinks/plugins/device_info_plus/ios`) + - firebase_auth (from `.symlinks/plugins/firebase_auth/ios`) + - firebase_core (from `.symlinks/plugins/firebase_core/ios`) + - firebase_messaging (from `.symlinks/plugins/firebase_messaging/ios`) + - Flutter (from `Flutter`) + - flutter_image_compress_common (from `.symlinks/plugins/flutter_image_compress_common/ios`) + - flutter_local_notifications (from `.symlinks/plugins/flutter_local_notifications/ios`) + - flutter_secure_storage (from `.symlinks/plugins/flutter_secure_storage/ios`) + - google_sign_in_ios (from `.symlinks/plugins/google_sign_in_ios/darwin`) + - image_cropper (from `.symlinks/plugins/image_cropper/ios`) + - image_picker_ios (from `.symlinks/plugins/image_picker_ios/ios`) + - path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`) + - permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`) + - share_plus (from `.symlinks/plugins/share_plus/ios`) + - sqflite_darwin (from `.symlinks/plugins/sqflite_darwin/darwin`) + - url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`) + - vibration (from `.symlinks/plugins/vibration/ios`) + +SPEC REPOS: + trunk: + - AppAuth + - AppCheckCore + - Firebase + - FirebaseAppCheckInterop + - FirebaseAuth + - FirebaseAuthInterop + - FirebaseCore + - FirebaseCoreExtension + - FirebaseCoreInternal + - FirebaseInstallations + - FirebaseMessaging + - GoogleDataTransport + - GoogleSignIn + - GoogleUtilities + - GTMAppAuth + - GTMSessionFetcher + - libwebp + - Mantle + - nanopb + - PromisesObjC + - RecaptchaInterop + - SDWebImage + - SDWebImageWebPCoder + - TOCropViewController + +EXTERNAL SOURCES: + device_info_plus: + :path: ".symlinks/plugins/device_info_plus/ios" + firebase_auth: + :path: ".symlinks/plugins/firebase_auth/ios" + firebase_core: + :path: ".symlinks/plugins/firebase_core/ios" + firebase_messaging: + :path: ".symlinks/plugins/firebase_messaging/ios" + Flutter: + :path: Flutter + flutter_image_compress_common: + :path: ".symlinks/plugins/flutter_image_compress_common/ios" + flutter_local_notifications: + :path: ".symlinks/plugins/flutter_local_notifications/ios" + flutter_secure_storage: + :path: ".symlinks/plugins/flutter_secure_storage/ios" + google_sign_in_ios: + :path: ".symlinks/plugins/google_sign_in_ios/darwin" + image_cropper: + :path: ".symlinks/plugins/image_cropper/ios" + image_picker_ios: + :path: ".symlinks/plugins/image_picker_ios/ios" + path_provider_foundation: + :path: ".symlinks/plugins/path_provider_foundation/darwin" + permission_handler_apple: + :path: ".symlinks/plugins/permission_handler_apple/ios" + share_plus: + :path: ".symlinks/plugins/share_plus/ios" + sqflite_darwin: + :path: ".symlinks/plugins/sqflite_darwin/darwin" + url_launcher_ios: + :path: ".symlinks/plugins/url_launcher_ios/ios" + vibration: + :path: ".symlinks/plugins/vibration/ios" + +SPEC CHECKSUMS: + AppAuth: 1c1a8afa7e12f2ec3a294d9882dfa5ab7d3cb063 + AppCheckCore: cc8fd0a3a230ddd401f326489c99990b013f0c4f + device_info_plus: 21fcca2080fbcd348be798aa36c3e5ed849eefbe + Firebase: 800d487043c0557d9faed71477a38d9aafb08a41 + firebase_auth: 5a5603bbe7fc673f88b7c652bf9c41d6c742b545 + firebase_core: 633e1851ffe1b9ab875f6467a4f574c79cef02e4 + firebase_messaging: d17feef781edc84ebefe62624fb384358ad96361 + FirebaseAppCheckInterop: c848d06a04030c9858ef0ae555b82035dbe470d0 + FirebaseAuth: 654e4de84787c45d7265599a651038e854ccb439 + FirebaseAuthInterop: 002da671896af5e8879ae117dc604ed240b86e80 + FirebaseCore: 055f4ab117d5964158c833f3d5e7ec6d91648d4a + FirebaseCoreExtension: 639afb3de6abd611952be78a794c54a47fa0f361 + FirebaseCoreInternal: dedc28e569a4be85f38f3d6af1070a2e12018d55 + FirebaseInstallations: d4c7c958f99c8860d7fcece786314ae790e2f988 + FirebaseMessaging: af49f8d7c0a3d2a017d9302c80946f45a7777dde + Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7 + flutter_image_compress_common: 1697a328fd72bfb335507c6bca1a65fa5ad87df1 + flutter_local_notifications: a5a732f069baa862e728d839dd2ebb904737effb + flutter_secure_storage: 1ed9476fba7e7a782b22888f956cce43e2c62f13 + google_sign_in_ios: 205742c688aea0e64db9da03c33121694a365109 + GoogleDataTransport: aae35b7ea0c09004c3797d53c8c41f66f219d6a7 + GoogleSignIn: c7f09cfbc85a1abf69187be091997c317cc33b77 + GoogleUtilities: 00c88b9a86066ef77f0da2fab05f65d7768ed8e1 + GTMAppAuth: 217a876b249c3c585a54fd6f73e6b58c4f5c4238 + GTMSessionFetcher: 5aea5ba6bd522a239e236100971f10cb71b96ab6 + image_cropper: c4326ea50132b1e1564499e5d32a84f01fb03537 + image_picker_ios: 7fe1ff8e34c1790d6fff70a32484959f563a928a + libwebp: 02b23773aedb6ff1fd38cec7a77b81414c6842a8 + Mantle: c5aa8794a29a022dfbbfc9799af95f477a69b62d + nanopb: fad817b59e0457d11a5dfbde799381cd727c1275 + path_provider_foundation: 080d55be775b7414fd5a5ef3ac137b97b097e564 + permission_handler_apple: 4ed2196e43d0651e8ff7ca3483a069d469701f2d + PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47 + RecaptchaInterop: 11e0b637842dfb48308d242afc3f448062325aba + SDWebImage: 9f177d83116802728e122410fb25ad88f5c7608a + SDWebImageWebPCoder: e38c0a70396191361d60c092933e22c20d5b1380 + share_plus: de6030e33b4e106470e09322d87cf2a4258d2d1d + sqflite_darwin: 20b2a3a3b70e43edae938624ce550a3cbf66a3d0 + TOCropViewController: 80b8985ad794298fb69d3341de183f33d1853654 + url_launcher_ios: 694010445543906933d732453a59da0a173ae33d + vibration: 69774ad57825b11c951ee4c46155f455d7a592ce + +PODFILE CHECKSUM: 53a6aebc29ccee84c41f92f409fc20cd4ca011f1 + +COCOAPODS: 1.16.2 diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index 9529e80..f57f20b 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -14,7 +14,9 @@ 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; + AF0AF264D72AAB5C072EBF63 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D353C14D406BEA76DDC4CEDD /* Pods_Runner.framework */; }; ED1F6F4084CE38C57593745C /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = F533C517F5928BDCFDC9295F /* GoogleService-Info.plist */; }; + F6E92F32EE05FDB601BF08FD /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1A63E609133A89EEF042220B /* Pods_RunnerTests.framework */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -43,6 +45,8 @@ /* Begin PBXFileReference section */ 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; + 1A63E609133A89EEF042220B /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 28D4768B792FCD16018172CA /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; 331C807B294A618700263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = ""; }; 331C8081294A63A400263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; @@ -56,6 +60,12 @@ 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + A16DB7C708FA3D7E4D68FD93 /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = ""; }; + B8A00C457D5850C21DE865E2 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; + BF0866B0BBB709298D77AE01 /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = ""; }; + D34871C8E2C1B2113E855908 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; + D353C14D406BEA76DDC4CEDD /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + EEAEE25FE2EFA1AE050FF002 /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = ""; }; F533C517F5928BDCFDC9295F /* GoogleService-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; name = "GoogleService-Info.plist"; path = "Runner/GoogleService-Info.plist"; sourceTree = ""; }; /* End PBXFileReference section */ @@ -64,6 +74,15 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + AF0AF264D72AAB5C072EBF63 /* Pods_Runner.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + DF4E3F6FDE887838D39DB349 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + F6E92F32EE05FDB601BF08FD /* Pods_RunnerTests.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -78,6 +97,29 @@ path = RunnerTests; sourceTree = ""; }; + 3AF11E78240F2F4936A829ED /* Frameworks */ = { + isa = PBXGroup; + children = ( + D353C14D406BEA76DDC4CEDD /* Pods_Runner.framework */, + 1A63E609133A89EEF042220B /* Pods_RunnerTests.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + 92F26F512BB0C437BB6F8B84 /* Pods */ = { + isa = PBXGroup; + children = ( + 28D4768B792FCD16018172CA /* Pods-Runner.debug.xcconfig */, + D34871C8E2C1B2113E855908 /* Pods-Runner.release.xcconfig */, + B8A00C457D5850C21DE865E2 /* Pods-Runner.profile.xcconfig */, + A16DB7C708FA3D7E4D68FD93 /* Pods-RunnerTests.debug.xcconfig */, + BF0866B0BBB709298D77AE01 /* Pods-RunnerTests.release.xcconfig */, + EEAEE25FE2EFA1AE050FF002 /* Pods-RunnerTests.profile.xcconfig */, + ); + name = Pods; + path = Pods; + sourceTree = ""; + }; 9740EEB11CF90186004384FC /* Flutter */ = { isa = PBXGroup; children = ( @@ -97,6 +139,8 @@ 97C146EF1CF9000F007C117D /* Products */, 331C8082294A63A400263BE5 /* RunnerTests */, F533C517F5928BDCFDC9295F /* GoogleService-Info.plist */, + 92F26F512BB0C437BB6F8B84 /* Pods */, + 3AF11E78240F2F4936A829ED /* Frameworks */, ); sourceTree = ""; }; @@ -131,8 +175,10 @@ isa = PBXNativeTarget; buildConfigurationList = 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */; buildPhases = ( + 84654B32CEBA94A3AB98B1F1 /* [CP] Check Pods Manifest.lock */, 331C807D294A63A400263BE5 /* Sources */, 331C807F294A63A400263BE5 /* Resources */, + DF4E3F6FDE887838D39DB349 /* Frameworks */, ); buildRules = ( ); @@ -148,12 +194,15 @@ isa = PBXNativeTarget; buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; buildPhases = ( + A2F48F17CEBC9FC6CBB4FD8B /* [CP] Check Pods Manifest.lock */, 9740EEB61CF901F6004384FC /* Run Script */, 97C146EA1CF9000F007C117D /* Sources */, 97C146EB1CF9000F007C117D /* Frameworks */, 97C146EC1CF9000F007C117D /* Resources */, 9705A1C41CF9048500538489 /* Embed Frameworks */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */, + 5D3AD08720B484D33EA3F7F2 /* [CP] Embed Pods Frameworks */, + 639726CC45A829220CA89AB2 /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -242,6 +291,62 @@ shellPath = /bin/sh; shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; }; + 5D3AD08720B484D33EA3F7F2 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; + 639726CC45A829220CA89AB2 /* [CP] Copy Pods Resources */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Copy Pods Resources"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n"; + showEnvVarsInLog = 0; + }; + 84654B32CEBA94A3AB98B1F1 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; 9740EEB61CF901F6004384FC /* Run Script */ = { isa = PBXShellScriptBuildPhase; alwaysOutOfDate = 1; @@ -257,6 +362,28 @@ shellPath = /bin/sh; shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; }; + A2F48F17CEBC9FC6CBB4FD8B /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ @@ -383,6 +510,7 @@ }; 331C8088294A63A400263BE5 /* Debug */ = { isa = XCBuildConfiguration; + baseConfigurationReference = A16DB7C708FA3D7E4D68FD93 /* Pods-RunnerTests.debug.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; @@ -400,6 +528,7 @@ }; 331C8089294A63A400263BE5 /* Release */ = { isa = XCBuildConfiguration; + baseConfigurationReference = BF0866B0BBB709298D77AE01 /* Pods-RunnerTests.release.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; @@ -415,6 +544,7 @@ }; 331C808A294A63A400263BE5 /* Profile */ = { isa = XCBuildConfiguration; + baseConfigurationReference = EEAEE25FE2EFA1AE050FF002 /* Pods-RunnerTests.profile.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; diff --git a/ios/Runner.xcworkspace/contents.xcworkspacedata b/ios/Runner.xcworkspace/contents.xcworkspacedata index 1d526a1..21a3cc1 100644 --- a/ios/Runner.xcworkspace/contents.xcworkspacedata +++ b/ios/Runner.xcworkspace/contents.xcworkspacedata @@ -4,4 +4,7 @@ + + diff --git a/lib/constant/box_name.dart b/lib/constant/box_name.dart index a37d864..e88a7c3 100644 --- a/lib/constant/box_name.dart +++ b/lib/constant/box_name.dart @@ -8,6 +8,7 @@ class BoxName { static const String jwt = "jwt"; static const String fingerPrint = "fingerPrint"; 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"; diff --git a/lib/constant/links.dart b/lib/constant/links.dart index 8842af6..af56266 100644 --- a/lib/constant/links.dart +++ b/lib/constant/links.dart @@ -9,12 +9,20 @@ class AppLink { 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 location = 'https://api.intaleq.xyz/intaleq/ride/location'; + static String locationServer = + 'https://location.intaleq.xyz/intaleq/ride/location'; + static String seferPaymentServer0 = box.read('seferPaymentServer'); + + static final String endPoint = 'https://api.intaleq.xyz/intaleq'; + 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://intaleq.xyz/intaleq'; + static final String server = 'https://api.intaleq.xyz/intaleq'; static final String jwtService = '$server/jwtService.php'; - static final String endPoint = box.read(BoxName.serverChosen); + // 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/'; @@ -46,16 +54,26 @@ class AppLink { static String getAccount = "$authCaptin/getAccount.php"; static String test = "$server/test.php"; static String serviceApp = "$server/serviceapp"; - static String getPassengersByPhone = - "$server/serviceApp/getPassengersByPhone.php"; + 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 = diff --git a/lib/controller/firbase_messge.dart b/lib/controller/firbase_messge.dart new file mode 100644 index 0000000..bccff4d --- /dev/null +++ b/lib/controller/firbase_messge.dart @@ -0,0 +1,105 @@ +import 'dart:io'; +import 'package:firebase_messaging/firebase_messaging.dart'; +import 'package:get/get.dart'; +import 'package:service/controller/local_notification.dart'; +import '../../constant/box_name.dart'; +import '../../main.dart'; +import '../../print.dart'; + +class FirebaseMessagesController extends GetxController { + final fcmToken = FirebaseMessaging.instance; + + List tokens = []; + List dataTokens = []; + late String driverID; + late String driverToken; + NotificationSettings? notificationSettings; + + Future 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 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() + // ? Get.find() + // : 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" حسب نوع المستخدم + print("Subscribed to 'service' topic ✅"); + + 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); + } + }); + } + + Future 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'); + } + } + } +} diff --git a/lib/controller/functions/crud.dart b/lib/controller/functions/crud.dart index d7e1c3c..66e74c2 100644 --- a/lib/controller/functions/crud.dart +++ b/lib/controller/functions/crud.dart @@ -17,35 +17,25 @@ import 'initilize.dart'; class CRUD { var dev; - getJWT() async { + getJWT(String pass, email) async { var dev = Platform.isAndroid ? 'android' : 'ios'; var payload = { - 'password': AK.passnpassenger, - // 'email': box.read(BoxName.email), + 'password': box.read(BoxName.password) ?? pass, + 'email': box.read(BoxName.email) ?? email, 'aud': '${AK.allowed}$dev', }; - // if (box.read(BoxName.firstTimeLoadKey).toString() != 'false') { var response0 = await http.post( Uri.parse(AppLink.jwtService), body: payload, ); - print(response0.body); - print(response0.request); + if (response0.statusCode == 200) { final decodedResponse1 = jsonDecode(response0.body); final jwt = decodedResponse1['jwt']; + Log.print('jwt: ${jwt}'); box.write(BoxName.jwt, X.c(X.c(X.c(jwt, cn), cC), cs)); - - // await AppInitializer().getAIKey(Service.keyOfApp); - // await AppInitializer().getAIKey(Service.initializationVector); - // await Future.delayed(Duration.zero); - await EncryptionHelper.initialize(); - - // await AppInitializer().getAIKey(Service.FCM_PRIVATE_KEY); - box.write(BoxName.firstTimeLoadKey, 'false'); - // await AppInitializer().getKey(); - } else {} + } } Future get({ @@ -61,7 +51,7 @@ class CRUD { .split(AppInformation.addd)[0]); if (isTokenExpired) { - await getJWT(); + await getJWT(box.read(BoxName.password), box.read(BoxName.email)); } var response = await http.post( url, @@ -72,10 +62,13 @@ class CRUD { 'Bearer ${X.r(X.r(X.r(box.read(BoxName.jwt), cn), cC), cs).toString().split(AppInformation.addd)[0]}' }, ); - print(response.request); - Log.print('response.body: ${response.body}'); - print(payload); // if (response.statusCode == 200) { + Log.print('esponse.body: ${response.body}'); + Log.print('esponse.req: ${response.request}'); + Log.print('payload: ${payload}'); + var jsonData = jsonDecode(response.body); + + Log.print('jsonData: ${jsonData}'); if (jsonData['status'] == 'success') { return response.body; } @@ -179,7 +172,7 @@ class CRUD { .split(AppInformation.addd)[0]); if (isTokenExpired) { - await getJWT(); + await getJWT(box.read(BoxName.password), box.read(BoxName.email)); } var response = await http.post( url, @@ -190,10 +183,10 @@ class CRUD { 'Bearer ${X.r(X.r(X.r(box.read(BoxName.jwt), cn), cC), cs).toString().split(AppInformation.addd)[0]}' }, ); - print(response.request); - Log.print('response.body: ${response.body}'); - print(payload); + Log.print('req: ${response.request}'); + Log.print('res: ${response.body}'); var jsonData = jsonDecode(response.body); + if (response.statusCode == 200) { if (jsonData['status'] == 'success') { return response.body; diff --git a/lib/controller/functions/initilize.dart b/lib/controller/functions/initilize.dart index 26b1f25..7432739 100644 --- a/lib/controller/functions/initilize.dart +++ b/lib/controller/functions/initilize.dart @@ -15,19 +15,19 @@ import '../../print.dart'; class AppInitializer { List> links = []; - Future 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(); - } - } - } + // Future 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) { @@ -35,7 +35,6 @@ class AppInitializer { await CRUD().get(link: Env.getapiKey, payload: {"keyName": key1}); if (res != 'failure') { var d = jsonDecode(res)['message']; - Log.print('d: ${d}'); await storage.write(key: key1, value: d[key1].toString()); await Future.delayed(Duration.zero); } else {} diff --git a/lib/controller/local/translations.dart b/lib/controller/local/translations.dart index 4de89ba..7401ad4 100644 --- a/lib/controller/local/translations.dart +++ b/lib/controller/local/translations.dart @@ -46,9 +46,14 @@ class MyTranslation extends Translations { "Dark Red": "نبيتي", "Sky Blue": "أزرق سماوي", "Mocha": "موكا", + 'Drivers Activity': "نشاط السائقين", + 'Drivers phones not register': "هواتف السائقين غير مسجلة", + 'Register new driver': "تسجيل سائق جديد", "Champagne": "شامبان", + 'Drivers waitting Register': "السائقون في انتظار التسجيل", "Bronze": "برونزي", "Maroon": "ماروني", + 'Drivers Want Register': "السائقون يريدون التسجيل", "Capture an Image of Your Criminal Record": "التقط صورة لسجلك الجنائي", "IssueDate": "تاريخ الإصدار", diff --git a/lib/controller/local_notification.dart b/lib/controller/local_notification.dart new file mode 100644 index 0000000..bb4677a --- /dev/null +++ b/lib/controller/local_notification.dart @@ -0,0 +1,331 @@ +import 'dart:async'; +import 'dart:io'; + +import 'package:get/get.dart'; +import 'package:permission_handler/permission_handler.dart'; +import 'package:flutter_local_notifications/flutter_local_notifications.dart'; +import 'package:timezone/data/latest.dart' as tz; +import 'package:timezone/timezone.dart' as tz; + +import '../../main.dart'; + +class NotificationController extends GetxController { + final FlutterLocalNotificationsPlugin _flutterLocalNotificationsPlugin = + FlutterLocalNotificationsPlugin(); + + @override + void onInit() { + super.onInit(); + initNotifications(); + } + + // Initializes the local notifications plugin + Future 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 _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 _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'); + } +} diff --git a/lib/controller/login_controller.dart b/lib/controller/login_controller.dart index eca7c74..b58b0f3 100644 --- a/lib/controller/login_controller.dart +++ b/lib/controller/login_controller.dart @@ -5,10 +5,15 @@ import 'package:get/get.dart'; import 'package:service/constant/links.dart'; import 'package:service/controller/functions/crud.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'; +import 'functions/initilize.dart'; + class LoginController extends GetxController { var email = TextEditingController(); var password = TextEditingController(); @@ -21,28 +26,59 @@ class LoginController extends GetxController { String? storedEmail = await storage.read(key: 'email'); String? storedPassword = await storage.read(key: 'password'); + await CRUD() + .getJWT(storedPassword ?? password.text, storedEmail ?? email.text); + if (storedEmail != null) { - Get.off(() => Main()); + var payload = { + "email": storedEmail, + "password": storedPassword, + }; + Log.print('payload: ${payload}'); + var res = await CRUD().get(link: AppLink.login, payload: payload); + Log.print('res: ${res}'); + + // if (res != 'failure') { + var d = jsonDecode(res); + Log.print('d: ${d}'); + if (d['message'] == "Login successful") { + // Save data securely in FlutterSecureStorage + + await box.write(BoxName.employeename, d['data']['first_name']); + Get.off(() => Main()); + // } + } } else { if (formKey.currentState!.validate()) { - var res = await CRUD().get(link: AppLink.login, payload: { + var payload = { "email": storedEmail ?? email.text, "password": storedPassword ?? password.text, - }); + }; + Log.print('payload: ${payload}'); + var res = await CRUD().get(link: AppLink.login, payload: payload); + Log.print('res: ${res}'); - if (res != 'failure') { - var d = jsonDecode(res); - if (d['message'] == "Login successful") { - // Save data securely in FlutterSecureStorage - await storage.write(key: 'email', value: d['data']['email']); - await storage.write(key: 'name', value: d['data']['first_name']); - await storage.write(key: 'driverID', value: d['data']['id']); - await storage.write(key: 'password', value: password.text); + // if (res != 'failure') { + var d = jsonDecode(res); + Log.print('d: ${d}'); + if (d['message'] == "Login successful") { + // Save data securely in FlutterSecureStorage + await storage.write(key: 'email', value: d['data']['email']); + await storage.write(key: 'name', value: d['data']['first_name']); + await storage.write(key: 'driverID', value: d['data']['id']); + await storage.write(key: 'password', value: password.text); + await box.write(BoxName.employeename, d['data']['first_name']); - Get.off(() => Main()); - } + Get.off(() => Main()); + // } } } } } + + @override + void onInit() { + login(); + super.onInit(); + } } diff --git a/lib/controller/mainController/main_controller.dart b/lib/controller/mainController/main_controller.dart index e3e74ad..a659ce6 100644 --- a/lib/controller/mainController/main_controller.dart +++ b/lib/controller/mainController/main_controller.dart @@ -1,5 +1,6 @@ import 'dart:convert'; import 'dart:io'; +import 'dart:math'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; @@ -71,6 +72,27 @@ class MainController extends GetxController { } } + 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 makePhoneCall(String phoneNumber) async { final Uri launchUri = Uri( scheme: 'tel', @@ -79,25 +101,29 @@ class MainController extends GetxController { await launchUrl(launchUri); } - void launchCommunication( + Future launchCommunication( String method, String contactInfo, String message) async { - String url; + // رقّم فقط (بدون + أو مسافات) + final phone = contactInfo.replaceAll(RegExp(r'[^0-9]'), ''); + final encodedMsg = Uri.encodeComponent(message); + + Uri? uri; if (Platform.isIOS) { switch (method) { case 'phone': - url = 'tel:$contactInfo'; + uri = Uri.parse('tel:$phone'); break; case 'sms': - url = 'sms:$contactInfo?body=${Uri.encodeComponent(message)}'; + uri = Uri.parse('sms:$phone?body=$encodedMsg'); break; case 'whatsapp': - url = - 'https://api.whatsapp.com/send?phone=$contactInfo&text=${Uri.encodeComponent(message)}'; + uri = Uri.parse( + 'https://api.whatsapp.com/send?phone=$phone&text=$encodedMsg'); break; case 'email': - url = - 'mailto:$contactInfo?subject=Subject&body=${Uri.encodeComponent(message)}'; + uri = + Uri.parse('mailto:$contactInfo?subject=Subject&body=$encodedMsg'); break; default: return; @@ -105,27 +131,32 @@ class MainController extends GetxController { } else if (Platform.isAndroid) { switch (method) { case 'phone': - url = 'tel:$contactInfo'; + uri = Uri.parse('tel:$phone'); break; case 'sms': - url = 'sms:$contactInfo?body=${Uri.encodeComponent(message)}'; + uri = Uri.parse('sms:$phone?body=$encodedMsg'); 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)}'; + { + 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'; + } } - break; case 'email': - url = - 'mailto:$contactInfo?subject=Subject&body=${Uri.encodeComponent(message)}'; + uri = + Uri.parse('mailto:$contactInfo?subject=Subject&body=$encodedMsg'); break; default: return; @@ -134,9 +165,14 @@ class MainController extends GetxController { return; } - if (await canLaunchUrl(Uri.parse(url))) { - await launchUrl(Uri.parse(url)); - } else {} + if (uri != null) { + final ok = await canLaunchUrl(uri); + if (ok) { + await launchUrl(uri, mode: LaunchMode.externalApplication); + } else { + // ممكن تضيف Snackbar/Toast هنا + } + } } List driverNotCompleteRegistration = []; @@ -153,6 +189,48 @@ class MainController extends GetxController { } } + 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 = []; + getDriverWantCompleteRegistration() async { + var res = + await CRUD().get(link: AppLink.getDriversWaitingActive, payload: {}); + if (res != 'failure') { + var d = jsonDecode(res)['message']; + driverWantCompleteRegistration = d; + filteredDrivers = driverWantCompleteRegistration; + update(); + } else { + Get.snackbar(res, ''); + } + } + + List driversPhoneNotComplete = []; + getDriversPhoneNotComplete() async { + var res = + await CRUD().get(link: AppLink.getDriversPhoneNotComplete, payload: {}); + if (res != 'failure') { + var d = jsonDecode(res)['message']; + driverWantCompleteRegistration = d; + filteredDrivers = driverWantCompleteRegistration; + update(); + } else { + Get.snackbar(res, ''); + } + } + List newDriverRegister = []; getNewDriverRegister() async { var res = await CRUD().get(link: AppLink.getNewDriverRegister, payload: {}); @@ -364,6 +442,7 @@ class MainController extends GetxController { 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); @@ -387,6 +466,22 @@ class MainController extends GetxController { if (formKey.currentState!.validate()) { await getDriverByPhone(); Get.back(); + if (driverData.isEmpty) { + Get.snackbar('Error', 'Driver not found', backgroundColor: Colors.red); + return; + } + Get.to(() => DriverPage()); + } + } + + searchDriverByNational() async { + if (formKey.currentState!.validate()) { + await getDriverByNational(); + Get.back(); + if (driverData.isEmpty) { + Get.snackbar('Error', 'Driver not found', backgroundColor: Colors.red); + return; + } Get.to(() => DriverPage()); } } @@ -394,7 +489,7 @@ class MainController extends GetxController { getPassengersByPhone() async { var res = await CRUD().get( link: AppLink.getPassengersByPhone, - payload: {"phone": '+2${passengerPhoneController.text}'}); + payload: {"phone": passengerPhoneController.text}); if (res != 'failure') { var d = jsonDecode(res); @@ -406,12 +501,28 @@ class MainController extends GetxController { getDriverByPhone() async { var res = await CRUD().get( link: AppLink.getDriverByPhone, - payload: {"phone": '+2${driverPhoneController.text}'}); + payload: {"phone": driverPhoneController.text}); if (res != 'failure') { var d = jsonDecode(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 = jsonDecode(res); + driverData = d; + update(); + } else { + Get.snackbar('Error', 'Driver not found', backgroundColor: Colors.red); } } } diff --git a/lib/controller/mainController/pages/add_car.dart b/lib/controller/mainController/pages/add_car.dart index 89b6031..3787efe 100644 --- a/lib/controller/mainController/pages/add_car.dart +++ b/lib/controller/mainController/pages/add_car.dart @@ -92,7 +92,7 @@ class AddCarForm extends StatelessWidget { AppLink.uploadEgypt, carData['id'], 'car_front'); }, child: Image.network( - 'https://sefer.click/sefer/card_image/car_front-${carData['id']}.jpg', + '${AppLink.server}/card_image/car_front-${carData['id']}.jpg', height: 200, width: double.maxFinite, fit: BoxFit.fill, @@ -100,7 +100,7 @@ class AddCarForm extends StatelessWidget { StackTrace? stackTrace) { // If the image fails to load, use the _copy version return Image.network( - 'https://sefer.click/sefer/card_image/car_front-${carData['id']}_copy.jpg', + '${AppLink.server}/card_image/car_front-${carData['id']}_copy.jpg', height: 200, width: double.maxFinite, fit: BoxFit.fill, @@ -114,7 +114,7 @@ class AddCarForm extends StatelessWidget { AppLink.uploadEgypt, carData['id'], 'car_back'); }, child: Image.network( - 'https://sefer.click/sefer/card_image/car_back-${carData['id']}.jpg', + '${AppLink.server}/card_image/car_back-${carData['id']}.jpg', height: 200, width: double.maxFinite, fit: BoxFit.fill, @@ -122,7 +122,7 @@ class AddCarForm extends StatelessWidget { StackTrace? stackTrace) { // If the image fails to load, use the _copy version return Image.network( - 'https://sefer.click/sefer/card_image/car_back-${carData['id']}_copy.jpg', + '${AppLink.server}/card_image/car_back-${carData['id']}_copy.jpg', height: 200, width: double.maxFinite, fit: BoxFit.fill, diff --git a/lib/controller/mainController/pages/driver_page.dart b/lib/controller/mainController/pages/driver_page.dart index 5755aea..8cb8b2d 100644 --- a/lib/controller/mainController/pages/driver_page.dart +++ b/lib/controller/mainController/pages/driver_page.dart @@ -1,18 +1,20 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; -import 'package:service/views/widgets/my_scafold.dart'; import '../main_controller.dart'; class DriverPage extends StatelessWidget { DriverPage({super.key}); - MainController mainController = MainController(); + + final MainController mainController = Get.find(); + @override Widget build(BuildContext context) { return Scaffold( body: GetBuilder(builder: (mainController) { Map data = mainController.driverData['message'][0]; + return CupertinoPageScaffold( navigationBar: CupertinoNavigationBar( middle: Text('${data['first_name']} ${data['last_name']}'), @@ -21,16 +23,11 @@ class DriverPage extends StatelessWidget { child: CupertinoScrollbar( child: ListView( children: [ - _buildDriverInfoSection( - mainController.driverData['message'][0]), - _buildStatisticsSection( - mainController.driverData['message'][0]), - _buildCarInfoSection(mainController.driverData['message'][0]), - _buildLicenseInfoSection( - mainController.driverData['message'][0]), - _buildBankInfoSection( - mainController.driverData['message'][0]), - // buildCarInfo(mainController.driverData['message'][0]), + _buildDriverInfoSection(data), + _buildStatisticsSection(data), + _buildCarInfoSection(data), + _buildLicenseInfoSection(data), + _buildBankInfoSection(data), ], ), ), @@ -40,32 +37,83 @@ class DriverPage extends StatelessWidget { ); } - Widget _buildDriverInfoSection(Map data) { + // ============================================================ + // 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: [ - _buildInfoRow('Name'.tr, data['name_arabic'].toString()), - _buildInfoRow('Name (English)'.tr, data['name_english'].toString()), - _buildInfoRow('Phone'.tr, data['phone'].toString()), - _buildInfoRow('Email'.tr, data['email'].toString()), - _buildInfoRow('Gender'.tr, data['gender'].toString()), - _buildInfoRow('Birthdate'.tr, data['birthdate'].toString()), - _buildInfoRow('National Number'.tr, data['national_number'].toString()), - _buildInfoRow('Religion'.tr, data['religion'].toString()), - _buildInfoRow('Occupation'.tr, data['occupation'].toString()), - _buildInfoRow('Education'.tr, data['education'].toString()), + _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) { + 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']}'), - _buildInfoRow('Wallet Balance'.tr, '\$${data['totalDriverWallet']}'), + _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( @@ -76,53 +124,55 @@ class DriverPage extends StatelessWidget { ); } - Widget _buildCarInfoSection(Map data) { - return CupertinoListSection.insetGrouped( - header: Text('Vehicle Information'.tr), - children: [ - _buildInfoRow('VIN'.tr, data['vin'].toString()), - _buildInfoRow('Plate Number'.tr, data['car_plate'].toString()), - _buildInfoRow('Make'.tr, data['make'].toString()), - _buildInfoRow('Model'.tr, data['model'].toString()), - _buildInfoRow('Year'.tr, data['year'].toString()), - _buildInfoRow('Color'.tr, data['color'].toString()), - _buildInfoRow('Fuel Type'.tr, data['fuel'].toString()), - _buildInfoRow('Displacement'.tr, data['displacement'].toString()), - _buildInfoRow( - 'Registration Date'.tr, data['registration_date'].toString()), - _buildInfoRow('Expiration Date'.tr, data['expiration_date'].toString()), - ], - ); - } - - Widget _buildLicenseInfoSection(Map data) { - return CupertinoListSection.insetGrouped( - header: Text('License Information'.tr), - children: [ - _buildInfoRow('License Type'.tr, data['license_type'].toString()), - _buildInfoRow('Card ID'.tr, data['card_id'].toString()), - _buildInfoRow('Issue Date'.tr, data['issue_date'].toString()), - _buildInfoRow('Expiry Date'.tr, data['expiry_date'].toString()), - _buildInfoRow('Categories'.tr, data['license_categories'].toString()), - ], - ); - } - - Widget _buildBankInfoSection(Map data) { - return CupertinoListSection.insetGrouped( - header: Text('Bank Information'.tr), - children: [ - _buildInfoRow('Account Number'.tr, data['accountBank'].toString()), - _buildInfoRow('Bank Code'.tr, data['bankCode'].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)), + 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), + ], ); } } diff --git a/lib/controller/mainController/pages/drivers_cant_register.dart b/lib/controller/mainController/pages/drivers_cant_register.dart index 18cb9a9..c0a3a23 100644 --- a/lib/controller/mainController/pages/drivers_cant_register.dart +++ b/lib/controller/mainController/pages/drivers_cant_register.dart @@ -2,9 +2,10 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:service/constant/colors.dart'; -import 'package:service/controller/functions/encrypt_decrypt.dart'; import 'package:service/controller/mainController/main_controller.dart'; import 'package:service/views/widgets/my_scafold.dart'; +import 'package:service/main.dart'; +import 'package:service/constant/box_name.dart'; import 'registration_captain_page.dart'; @@ -14,201 +15,204 @@ class DriversCantRegister extends StatelessWidget { @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 Cant Register'.tr, + title: 'Drivers Want Register'.tr, isleading: true, body: [ GetBuilder(builder: (mainController) { return Column( children: [ + // Search Padding( - padding: const EdgeInsets.all(8.0), + 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: ListView.builder( - itemCount: mainController.filteredDrivers.length, - itemBuilder: (context, index) { - final driver = mainController.filteredDrivers[index]; - return Padding( - padding: const EdgeInsets.all(8.0), - child: Container( - color: driver['note'] == null - ? AppColor.greenColor - : AppColor.greyColor, - child: CupertinoFormSection( - header: Text( - 'Driver ID: ${driver['driverId']}', - ), - children: [ - InkWell( - onTap: () => mainController - .makePhoneCall(driver['phone_number']), - child: Container( - height: 40, - color: driver['note'] != null - ? AppColor.greenColor - : AppColor.greyColor, - child: Row( - mainAxisAlignment: - MainAxisAlignment.spaceAround, + 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: [ - Text((driver['phone_number'])), - IconButton( + 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" - "نلاحظ أنك لم تكمل عملية التسجيل في خدمة Tripz درايفر. نود تذكيرك بأن إكمال التسجيل يتيح لك فرصة الانضمام إلى فريق Tripz والاستفادة من خدماتنا المتنوعة.\n\n" - "إذا كنت بحاجة إلى أي مساعدة أو لديك أي استفسارات، لا تتردد في الاتصال بنا. نحن هنا لمساعدتك.\n\n" - "للاتصال بنا، يرجى الاتصال على الرقم التالي: +20 101 880 5430\n\n" - "مع تحيات فريق Tripz."; + "يظهر لدينا في نظام تطبيق *انطلق* أنك لم تكمل عملية التسجيل بعد.\n" + "ندعوك لإكمال التسجيل للاستفادة من مزايا التطبيق والبدء بالعمل معنا.\n\n" + "إذا احتجت لأي مساعدة، تواصل معنا على خدمة العملاء:\n" + "+963 952 475 742\n\n" + "+963 952 475 740\n\n" + "فريق انطلق يتمنى لك يوماً سعيداً."; mainController.launchCommunication( - 'whatsapp', - '${driver['phone_number']}', - message); + 'whatsapp', + driver['phone_number'], + message, + ); }, - icon: const Icon( - Icons.send_time_extension_sharp, - color: AppColor.secondaryColor, - ), ), - IconButton( + + // Edit button → go to registration form + buildActionButton( + icon: CupertinoIcons + .pencil_ellipsis_rectangle, + color: AppColor.gold, onPressed: () { - Get.to(() => RegisterCaptain(), - arguments: { - "phone_number": - driver['phone_number'] - .toString(), - 'driverId': driver['driverId'] - .toString(), - 'email': - driver['email'].toString(), - }); + 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']); }, - icon: const Icon( - Icons.save, - color: AppColor.gold, - ), ), ], - ) - // CupertinoFormRow( - // prefix: Text('Phone Number'.tr), - // child: CupertinoTextFormFieldRow( - // initialValue: driver['phone_number'], - // readOnly: true, - // placeholder: 'Phone Number'.tr, - // ), - // ), ), - ), - CupertinoFormRow( - prefix: Text('Created At'.tr), - child: CupertinoTextFormFieldRow( - initialValue: driver['created_at'], - readOnly: true, - placeholder: 'Created At', - ), - ), - CupertinoFormRow( - prefix: Text('Status'.tr), - child: GestureDetector( - onTap: () { - showCupertinoModalPopup( - context: Get.context!, - builder: (BuildContext context) => - Container( - height: 216, - padding: const EdgeInsets.only(top: 6.0), - margin: EdgeInsets.only( - bottom: MediaQuery.of(context) - .viewInsets - .bottom, - ), - color: CupertinoColors.systemBackground - .resolveFrom(context), - child: SafeArea( - top: false, - child: CupertinoPicker( - magnification: 1.22, - squeeze: 1.2, - useMagnifier: true, - itemExtent: 32.0, - scrollController: - FixedExtentScrollController( - initialItem: mainController - .selectedStatus - .indexOf(mainController - .selectedStatus), - ), - onSelectedItemChanged: - (int selectedItem) { - mainController.setSelectedStatus( - mainController - .statusOptions[selectedItem] - .tr); - }, - children: List.generate( - mainController.statusOptions - .length, (int index) { - return Center( - child: Text(mainController - .statusOptions[index].tr), - ); - }), - ), - ), - ), - ); - }, - child: CupertinoFormRow( - child: Text( - mainController.selectedStatus.tr, - style: TextStyle( - color: CupertinoColors.label - .resolveFrom(Get.context!)), - ), - ), - ), - ), - CupertinoFormRow( - prefix: Text('Notes'.tr), - child: CupertinoTextFormFieldRow( - cursorColor: AppColor.blueColor, - controller: mainController.notesController, - placeholder: - driver['note'] ?? "Additional comments".tr, - ), - ), - CupertinoButton( - child: Text('Save Notes'.tr), - onPressed: () { - // Save the notes for the driver - String notes = - mainController.notesController.text == '' - ? mainController.selectedStatus - .toString() - : mainController.notesController.text; - mainController - .saveNoteForDriverNotCompleteRegistration( - driver['phone_number'], - 'girls name', - notes); - print( - 'Notes for driver ${driver['id']}: $notes'); - }, + 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), + ), + ), + ], + ), ), - ], - ), + ); + }, ), - ); - }, - ), ), ], ); diff --git a/lib/controller/mainController/pages/new_driver.dart b/lib/controller/mainController/pages/new_driver.dart new file mode 100644 index 0000000..dcc8439 --- /dev/null +++ b/lib/controller/mainController/pages/new_driver.dart @@ -0,0 +1,485 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import 'package:service/constant/colors.dart'; +// Importa tu nuevo controlador de servicio +import 'package:service/controller/mainController/ragister_service_controller.dart'; +import 'package:service/views/widgets/elevated_btn.dart'; +import 'package: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 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( + 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( + 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( + 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( + 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( + // <-- 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( + 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), + ), + ); + }), + ), + ], + ); + } +} diff --git a/lib/controller/mainController/pages/passengers_cant_regster.dart b/lib/controller/mainController/pages/passengers_cant_regster.dart index b1fb48a..a7f3dd4 100644 --- a/lib/controller/mainController/pages/passengers_cant_regster.dart +++ b/lib/controller/mainController/pages/passengers_cant_regster.dart @@ -28,12 +28,12 @@ class PassengersCantRegister extends StatelessWidget { header: Text('Passenger ID: ${passenger['id']}'), children: [ InkWell( - onTap: () => mainController - .makePhoneCall(passenger['phone_number']), + onTap: () => + mainController.makePhoneCall(passenger['phone']), child: CupertinoFormRow( - prefix: Text('Phone Number'.tr), + prefix: Text(' phone'.tr), child: CupertinoTextFormFieldRow( - initialValue: ((passenger['phone_number'])), + initialValue: ((passenger['phone'])), readOnly: true, placeholder: 'Phone Number'.tr, ), diff --git a/lib/controller/mainController/pages/passengers_page.dart b/lib/controller/mainController/pages/passengers_page.dart index 036db8c..1d055ad 100644 --- a/lib/controller/mainController/pages/passengers_page.dart +++ b/lib/controller/mainController/pages/passengers_page.dart @@ -9,92 +9,109 @@ class PassengersPage extends StatelessWidget { @override Widget build(BuildContext context) { - return GetBuilder(builder: (mainController) { - Map data = mainController.passengerData['message'][0]; - return MyScaffold( - title: (data['first_name']?.toString() ?? '') + - ' ' + - (data['last_name']?.toString() ?? ''), - isleading: true, - body: [ - ListView( - children: [ - _buildPersonalInfoCard(data), - _buildLatestRideCard(data), - _buildWalletInfoCard(data), - ], - ), - ], - ); - }); + return GetBuilder( + 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 Card( - margin: const EdgeInsets.all(16), - child: Padding( - padding: const EdgeInsets.all(16), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text('Personal Information'.tr, - style: - const TextStyle(fontSize: 18, fontWeight: FontWeight.bold)), - const SizedBox(height: 16), - _buildInfoRow('Phone'.tr, data['phone']?.toString() ?? 'N/A'), - _buildInfoRow('Email'.tr, data['email']?.toString() ?? 'N/A'), - _buildInfoRow('Gender'.tr, data['gender']?.toString() ?? 'N/A'), - _buildInfoRow( - 'Birthdate'.tr, data['birthdate']?.toString() ?? 'N/A'), - _buildInfoRow( - 'Education'.tr, data['education']?.toString() ?? 'N/A'), - _buildInfoRow( - 'Employment'.tr, data['employmentType']?.toString() ?? 'N/A'), - _buildInfoRow('Marital Status'.tr, - data['maritalStatus']?.toString() ?? 'N/A'), - ], - ), - ), + 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 Card( - margin: const EdgeInsets.all(16), - child: Padding( - padding: const EdgeInsets.all(16), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text('Latest Ride'.tr, - style: - const TextStyle(fontSize: 18, fontWeight: FontWeight.bold)), - const SizedBox(height: 16), - _buildInfoRow('Ride ID'.tr, data['ride_id']?.toString() ?? 'N/A'), - _buildInfoRow('Date'.tr, data['ride_date']?.toString() ?? 'N/A'), - _buildInfoRow( - 'Start Time'.tr, data['ride_time']?.toString() ?? 'N/A'), - _buildInfoRow( - 'End Time'.tr, data['ride_endtime']?.toString() ?? 'N/A'), - _buildInfoRow( - 'Price'.tr, '\$${data['price']?.toString() ?? 'N/A'}'), - _buildInfoRow( - 'Status'.tr, data['ride_status']?.toString() ?? 'N/A'), - _buildInfoRow('Payment Method'.tr, - data['ride_payment_method']?.toString() ?? 'N/A'), - _buildInfoRow('Car Type'.tr, data['car_type']?.toString() ?? 'N/A'), - _buildInfoRow( - 'Distance'.tr, - data['distance'] != null - ? '${data['distance'].toStringAsFixed(2)} km' - : 'N/A'), - ], - ), - ), + 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 children}) { return Card( margin: const EdgeInsets.all(16), child: Padding( @@ -102,30 +119,26 @@ class PassengersPage extends StatelessWidget { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text('Wallet Information'.tr, + Text(title, style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold)), const SizedBox(height: 16), - _buildInfoRow('Wallet Balance'.tr, - '\$${data['passenger_wallet_balance']?.toString() ?? 'N/A'}'), - _buildInfoRow('Last Payment Amount'.tr, - '\$${data['passenger_payment_amount']?.toString() ?? 'N/A'}'), - _buildInfoRow('Last Payment Method'.tr, - data['passenger_payment_method']?.toString() ?? 'N/A'), + ...children, ], ), ), ); } - Widget _buildInfoRow(String label, String value) { + 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)), - Text(value), + Flexible(child: Text(displayValue, overflow: TextOverflow.ellipsis)), ], ), ); diff --git a/lib/controller/mainController/pages/registration_captain_page.dart b/lib/controller/mainController/pages/registration_captain_page.dart index 55bd59c..b9cda34 100644 --- a/lib/controller/mainController/pages/registration_captain_page.dart +++ b/lib/controller/mainController/pages/registration_captain_page.dart @@ -1,2676 +1,458 @@ -// ignore_for_file: unnecessary_null_comparison - +import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; -import 'package:intl/intl.dart'; +import 'package:service/constant/colors.dart'; +import 'package:service/views/widgets/elevated_btn.dart'; import 'package:service/views/widgets/my_scafold.dart'; -import '../../../../constant/colors.dart'; -import '../../../../constant/links.dart'; -import '../../../../constant/style.dart'; -import '../../../views/widgets/elevated_btn.dart'; -import '../../../views/widgets/mycircular.dart'; + import '../registration_captain_controller.dart'; class RegisterCaptain extends StatelessWidget { - RegisterCaptain({super.key}); + const RegisterCaptain({super.key}); @override Widget build(BuildContext context) { final controller = Get.put(RegisterCaptainController()); - // String text = ''; - controller.driveInit(); - // final inspectionDate = - // controller.responseIdCardDriverEgyptBack['inspection_date'].toString(); - // final year = int.parse(inspectionDate.split('-')[0]); - // final inspectionDateTime = DateTime(year, 12, 31); return MyScaffold( - title: 'Documents check'.tr, - action: GetBuilder(builder: (controller) { - return IconButton( - onPressed: () { - controller.isLoading = false; - controller.update(); - }, - icon: const Icon(Icons.refresh), + 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), + ], ); }), - body: [ - GetBuilder(builder: (controller) { - return controller.isLoading - ? const MyCircularProgressIndicator() - : Padding( - padding: const EdgeInsets.all(8.0), - child: Column( - children: [ - (controller.responseIdCardDriverEgyptBack.isNotEmpty && - controller.responseIdCardDriverEgyptFront - .isNotEmpty && - controller.responseIdEgyptFront.isNotEmpty && - controller.responseIdEgyptBack.isNotEmpty && - controller - .responseIdEgyptDriverLicense.isNotEmpty - // && - // controller - // .responseCriminalRecordEgypt.isNotEmpty - ) - ? MyElevatedButton( - title: 'Next'.tr, - onPressed: () { - controller.addDriverAndCarEgypt(); - }) - : const SizedBox(), - Row( - children: [ - handleInsert(controller), - MyElevatedButton( - kolor: controller.isEgypt - ? AppColor.greenColor - : AppColor.redColor, - title: controller.isEgypt - ? 'For Egypt'.tr - : 'Non Egypt'.tr, - onPressed: () { - controller.changeNationality(); - }) - ], - ), - SizedBox( - height: Get.height * .7, - child: ListView( - children: [ - egyptDriverLicense(), - egyptCarLicenceFront(), - egyptCarLicenceBack(), - controller.isEgypt - ? egyptDriverIDFront() - : nonEgyptDriverIDFront(), - controller.isEgypt - ? egyptDriverIDBack() - : nonEgyptDriverIDBack(), - // egyptCriminalRecord(), - ], - ), - ), - ], - ), - ); - }), - ], - isleading: true); + ], + ); } - // TextButton handleInsert(RegisterCaptainController controller) { - // return TextButton( - // onPressed: () { - // Get.to(MyScaffold(title: 'title', isleading: true, body: [ - // Column( - // children: [ - // SizedBox( - // height: Get.height * .8, - // child: Form( - // child: ListView( - // children: [ - // TextFormField( - // initialValue: - // controller.responseIdEgyptDriverLicense != null - // ? controller - // .responseIdEgyptDriverLicense['firstName'] - // ?.toString() - // : controller.firstName.value, - // decoration: - // const InputDecoration(labelText: 'First Name'), - // onChanged: (value) => - // controller.firstName.value = value, - // ), - // TextFormField( - // initialValue: - // controller.responseIdEgyptDriverLicense != null - // ? controller - // .responseIdEgyptDriverLicense['lastName'] - // ?.toString() - // : controller.lastName.value, - // decoration: - // const InputDecoration(labelText: 'Last Name'), - // onChanged: (value) => controller.lastName.value = value, - // ), - // TextFormField( - // initialValue: controller.responseIdEgyptBack != null - // ? controller.responseIdEgyptBack['gender'] - // ?.toString() - // : controller.gender.value, - // decoration: const InputDecoration(labelText: 'Gender'), - // onChanged: (value) => controller.gender.value = value, - // ), - // TextFormField( - // initialValue: controller.responseIdEgyptDriverLicense != - // null - // ? controller - // .responseIdEgyptDriverLicense['license_type'] - // ?.toString() - // : controller.licenseType.value, - // decoration: - // const InputDecoration(labelText: 'License Type'), - // onChanged: (value) => - // controller.licenseType.value = value, - // ), - // TextFormField( - // initialValue: controller.responseIdEgyptDriverLicense != - // null - // ? controller - // .responseIdEgyptDriverLicense['national_number'] - // ?.toString() - // : controller.nationalNumber.value, - // decoration: - // const InputDecoration(labelText: 'National Number'), - // onChanged: (value) => - // controller.nationalNumber.value = value, - // ), - // TextFormField( - // initialValue: - // controller.responseIdEgyptDriverLicense != null - // ? controller - // .responseIdEgyptDriverLicense['name_arabic'] - // ?.toString() - // : controller.nameArabic.value, - // decoration: - // const InputDecoration(labelText: 'Name in Arabic'), - // onChanged: (value) => - // controller.nameArabic.value = value, - // ), - // TextFormField( - // initialValue: controller.responseIdEgyptDriverLicense != - // null - // ? controller - // .responseIdEgyptDriverLicense['name_english'] - // ?.toString() - // : controller.nameEnglish.value, - // decoration: - // const InputDecoration(labelText: 'Name in English'), - // onChanged: (value) => - // controller.nameEnglish.value = value, - // ), - // TextFormField( - // initialValue: - // controller.responseIdEgyptDriverLicense != null - // ? controller - // .responseIdEgyptDriverLicense['issue_date'] - // ?.toString() - // : controller.issueDate.value, - // decoration: - // const InputDecoration(labelText: 'Issue Date'), - // onChanged: (value) => - // controller.issueDate.value = value, - // ), - // TextFormField( - // initialValue: - // controller.responseIdEgyptDriverLicense != null - // ? controller - // .responseIdEgyptDriverLicense['expiry_date'] - // ?.toString() - // : controller.expirationDate.value, - // decoration: - // const InputDecoration(labelText: 'Expiry Date'), - // onChanged: (value) => - // controller.expiryDate.value = value, - // ), - // TextFormField( - // initialValue: controller.responseIdEgyptFront != null - // ? controller.responseIdEgyptDriverLicense[ - // 'license_categories'] is List - // ? controller.responseIdEgyptDriverLicense[ - // 'license_categories'] - // .join(', ') - // : controller.responseIdEgyptDriverLicense[ - // 'license_categories'] - // ?.toString() ?? - // 'Not specified' - // : controller.licenseCategories.value, - // decoration: const InputDecoration( - // labelText: 'License Categories'), - // onChanged: (value) => - // controller.licenseCategories.value = value, - // ), - // TextFormField( - // initialValue: controller.responseIdEgyptFront != null - // ? controller.responseIdEgyptFront['address'] - // ?.toString() - // : controller.fuel.value, - // decoration: const InputDecoration(labelText: 'Address'), - // onChanged: (value) => controller.address.value = value, - // ), - // TextFormField( - // initialValue: controller.responseIdEgyptFront != null - // ? controller.responseIdEgyptFront['card_id'] - // ?.toString() - // : controller.cardId.value, - // decoration: const InputDecoration(labelText: 'Card ID'), - // onChanged: (value) => controller.cardId.value = value, - // ), - // TextFormField( - // initialValue: controller.responseIdEgyptBack != null - // ? controller.responseIdEgyptBack['occupation'] - // ?.toString() - // : controller.education.value, - // decoration: - // const InputDecoration(labelText: 'Occupation'), - // onChanged: (value) => - // controller.occupation.value = value, - // ), - // TextFormField( - // initialValue: controller.responseIdEgyptBack != null - // ? controller.responseIdEgyptBack['occupation'] - // ?.toString() - // : controller.occupation.value, - // decoration: - // const InputDecoration(labelText: 'Education'), - // onChanged: (value) => - // controller.education.value = value, - // ), - // TextFormField( - // initialValue: controller.responseIdEgyptBack != null - // ? controller.responseIdEgyptBack['issue_date'] - // ?.toString() - // : controller.issueDate.value, - // decoration: const InputDecoration( - // labelText: 'License Issue Date'), - // onChanged: (value) => - // controller.licenseIssueDate.value = value, - // ), - // TextFormField( - // initialValue: controller.responseIdEgyptBack != null - // ? controller.responseIdEgyptBack['religion'] - // ?.toString() - // : controller.religion.value, - // decoration: - // const InputDecoration(labelText: 'Religion'), - // onChanged: (value) => controller.religion.value = value, - // ), - // TextFormField( - // initialValue: controller.responseIdEgyptBack != null - // ? controller.responseIdEgyptBack['maritalStatus'] - // ?.toString() - // : controller.maritalStatus.value, - // decoration: - // const InputDecoration(labelText: 'Marital Status'), - // onChanged: (value) => - // controller.maritalStatus.value = value, - // ), - // TextFormField( - // initialValue: controller.responseIdEgyptDriverLicense != - // null - // ? controller.responseIdEgyptDriverLicense['address'] - // ?.toString() - // : controller.address.value, - // decoration: const InputDecoration(labelText: 'Site'), - // onChanged: (value) => controller.site.value = value, - // ), - // TextFormField( - // initialValue: controller.responseIdEgyptDriverLicense != - // null - // ? controller - // .responseIdEgyptDriverLicense['employmentType'] - // ?.toString() - // : controller.employmentType.value, - // decoration: - // const InputDecoration(labelText: 'Employment Type'), - // onChanged: (value) => - // controller.employmentType.value = value, - // ), - // const Divider( - // thickness: 5, - // height: 5, - // ), - // Text('Car Details'.tr), - // const Divider( - // thickness: 5, - // height: 5, - // ), - // TextFormField( - // initialValue: controller.vin.value.isEmpty - // ? controller - // .responseIdCardDriverEgyptBack['chassis'] - // ?.toString() ?? - // '' - // : controller.vin.value, - // decoration: const InputDecoration(labelText: 'VIN'), - // onChanged: (value) => controller.vin.value = value, - // ), - // TextFormField( - // initialValue: controller.vin.value.isEmpty - // ? controller - // .responseIdCardDriverEgyptBack['car_plate'] - // ?.toString() ?? - // '' - // : controller.vin.value, - // decoration: - // const InputDecoration(labelText: 'car_plate'), - // onChanged: (value) => controller.vin.value = value, - // ), - // TextFormField( - // initialValue: controller.make.value.isEmpty - // ? controller.responseIdCardDriverEgyptBack['make'] - // ?.toString() ?? - // '' - // : controller.make.value, - // decoration: const InputDecoration(labelText: 'Make'), - // onChanged: (value) => controller.make.value = value, - // ), - // TextFormField( - // initialValue: controller.model.value.isEmpty - // ? controller.responseIdCardDriverEgyptBack['model'] - // ?.toString() ?? - // '' - // : controller.model.value, - // decoration: const InputDecoration(labelText: 'Model'), - // onChanged: (value) => controller.model.value = value, - // ), - // TextFormField( - // initialValue: controller.year.value.isEmpty - // ? controller.responseIdCardDriverEgyptBack['year'] - // ?.toString() ?? - // '' - // : controller.year.value, - // decoration: const InputDecoration(labelText: 'Year'), - // onChanged: (value) => controller.year.value = value, - // ), - // TextFormField( - // initialValue: controller.displacement.value.isEmpty - // ? controller.responseIdCardDriverEgyptBack['engine'] - // ?.toString() ?? - // '' - // : controller.displacement.value, - // decoration: - // const InputDecoration(labelText: 'Displacement'), - // onChanged: (value) => - // controller.displacement.value = value, - // ), - // TextFormField( - // initialValue: controller.fuel.value.isEmpty - // ? controller.responseIdCardDriverEgyptBack['fuel'] - // ?.toString() ?? - // '' - // : controller.fuel.value, - // decoration: const InputDecoration(labelText: 'Fuel'), - // onChanged: (value) => controller.fuel.value = value, - // ), - // DropdownButtonFormField( - // decoration: InputDecoration( - // labelText: 'Color'.tr, // Localized label - // ), - // value: controller.color.value.isEmpty - // ? null - // : controller.color.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( - // value: colorValue, - // child: Text(colorName), - // ); - // }).toList(), - // onChanged: (value) { - // controller.color.value = value ?? ''; - // controller.colorHex.value = value ?? ''; - // }, - // ), - // TextFormField( - // initialValue: controller.owner.value.isNotEmpty - // ? controller.responseIdCardDriverEgyptFront['owner'] - // ?.toString() - // : controller.owner.value, - // decoration: const InputDecoration(labelText: 'Owner'), - // onChanged: (value) => controller.owner.value = value, - // ), - // TextFormField( - // initialValue: controller.expirationDate.value.isNotEmpty - // ? controller.responseIdCardDriverEgyptFront[ - // 'expiration_date'] - // ?.toString() - // : controller.owner.value, - // decoration: - // const InputDecoration(labelText: 'expiration_date'), - // onChanged: (value) => - // controller.expirationDate.value = value, - // ), - // TextFormField( - // initialValue: controller.addressCar.isNotEmpty - // ? controller - // .responseIdCardDriverEgyptFront['address'] - // ?.toString() - // : controller.addressCar.value, - // decoration: const InputDecoration(labelText: 'Address'), - // onChanged: (value) => - // controller.addressCar.value = value, - // ), - // // TextFormField( - // // initialValue: inspectionDateTime - // // .toString(), - // // decoration: - // // const InputDecoration( - // // labelText: - // // 'Registration Date'), - // // onChanged: (value) => controller - // // .registrationDate - // // .value = value, - // // ), - // const SizedBox(height: 20), - // ], - // ), - // ), - // ), - // MyElevatedButton( - // title: 'Upload'.tr, - // onPressed: () async { - // await controller.addDriverEgyptHanding(); - // await controller.addRegistrationCarEgyptHandling(); - // // await controller - // // .getDriverNotCompleteRegistration(); - // }, - // kolor: AppColor.greenColor, - // ), - // ], - // ), - // ])); - // // Get.defaultDialog( - // // barrierDismissible: false, - // // title: 'Insert all fields'.tr, - // // content: SizedBox( - // // height: 400, - // // width: 300, - // // child: Form( - // // child: ListView( - // // children: [ - // // TextFormField( - // // initialValue: - // // controller.responseIdEgyptDriverLicense != - // // null - // // ? controller - // // .responseIdEgyptDriverLicense[ - // // 'firstName'] - // // ?.toString() - // // : controller.firstName.value, - // // decoration: const InputDecoration( - // // labelText: 'First Name'), - // // onChanged: (value) => controller - // // .firstName.value = value, - // // ), - // // TextFormField( - // // initialValue: - // // controller.responseIdEgyptDriverLicense != - // // null - // // ? controller - // // .responseIdEgyptDriverLicense[ - // // 'lastName'] - // // ?.toString() - // // : controller.lastName.value, - // // decoration: const InputDecoration( - // // labelText: 'Last Name'), - // // onChanged: (value) => - // // controller.lastName.value = value, - // // ), - // // TextFormField( - // // initialValue: controller - // // .responseIdEgyptBack != - // // null - // // ? controller - // // .responseIdEgyptBack['gender'] - // // ?.toString() - // // : controller.gender.value, - // // decoration: const InputDecoration( - // // labelText: 'Gender'), - // // onChanged: (value) => - // // controller.gender.value = value, - // // ), - // // TextFormField( - // // initialValue: - // // controller.responseIdEgyptDriverLicense != - // // null - // // ? controller - // // .responseIdEgyptDriverLicense[ - // // 'license_type'] - // // ?.toString() - // // : controller - // // .licenseType.value, - // // decoration: const InputDecoration( - // // labelText: 'License Type'), - // // onChanged: (value) => controller - // // .licenseType.value = value, - // // ), - // // TextFormField( - // // initialValue: - // // controller.responseIdEgyptDriverLicense != - // // null - // // ? controller - // // .responseIdEgyptDriverLicense[ - // // 'national_number'] - // // ?.toString() - // // : controller - // // .nationalNumber.value, - // // decoration: const InputDecoration( - // // labelText: 'National Number'), - // // onChanged: (value) => controller - // // .nationalNumber.value = value, - // // ), - // // TextFormField( - // // initialValue: - // // controller.responseIdEgyptDriverLicense != - // // null - // // ? controller - // // .responseIdEgyptDriverLicense[ - // // 'name_arabic'] - // // ?.toString() - // // : controller.nameArabic.value, - // // decoration: const InputDecoration( - // // labelText: 'Name in Arabic'), - // // onChanged: (value) => controller - // // .nameArabic.value = value, - // // ), - // // TextFormField( - // // initialValue: - // // controller.responseIdEgyptDriverLicense != - // // null - // // ? controller - // // .responseIdEgyptDriverLicense[ - // // 'name_english'] - // // ?.toString() - // // : controller - // // .nameEnglish.value, - // // decoration: const InputDecoration( - // // labelText: 'Name in English'), - // // onChanged: (value) => controller - // // .nameEnglish.value = value, - // // ), - // // TextFormField( - // // initialValue: - // // controller.responseIdEgyptDriverLicense != - // // null - // // ? controller - // // .responseIdEgyptDriverLicense[ - // // 'issue_date'] - // // ?.toString() - // // : controller.issueDate.value, - // // decoration: const InputDecoration( - // // labelText: 'Issue Date'), - // // onChanged: (value) => controller - // // .issueDate.value = value, - // // ), - // // TextFormField( - // // initialValue: - // // controller.responseIdEgyptDriverLicense != - // // null - // // ? controller - // // .responseIdEgyptDriverLicense[ - // // 'expiry_date'] - // // ?.toString() - // // : controller - // // .expirationDate.value, - // // decoration: const InputDecoration( - // // labelText: 'Expiry Date'), - // // onChanged: (value) => controller - // // .expiryDate.value = value, - // // ), - // // TextFormField( - // // initialValue: controller - // // .responseIdEgyptFront != - // // null - // // ? controller.responseIdEgyptDriverLicense[ - // // 'license_categories'] - // // is List - // // ? controller - // // .responseIdEgyptDriverLicense[ - // // 'license_categories'] - // // .join(', ') - // // : controller - // // .responseIdEgyptDriverLicense[ - // // 'license_categories'] - // // ?.toString() ?? - // // 'Not specified' - // // : controller - // // .licenseCategories.value, - // // decoration: const InputDecoration( - // // labelText: 'License Categories'), - // // onChanged: (value) => controller - // // .licenseCategories.value = value, - // // ), - // // TextFormField( - // // initialValue: controller - // // .responseIdEgyptFront != - // // null - // // ? controller.responseIdEgyptFront[ - // // 'address'] - // // ?.toString() - // // : controller.fuel.value, - // // decoration: const InputDecoration( - // // labelText: 'Address'), - // // onChanged: (value) => - // // controller.address.value = value, - // // ), - // // TextFormField( - // // initialValue: controller - // // .responseIdEgyptFront != - // // null - // // ? controller.responseIdEgyptFront[ - // // 'card_id'] - // // ?.toString() - // // : controller.cardId.value, - // // decoration: const InputDecoration( - // // labelText: 'Card ID'), - // // onChanged: (value) => - // // controller.cardId.value = value, - // // ), - // // TextFormField( - // // initialValue: controller - // // .responseIdEgyptBack != - // // null - // // ? controller.responseIdEgyptBack[ - // // 'occupation'] - // // ?.toString() - // // : controller.education.value, - // // decoration: const InputDecoration( - // // labelText: 'Occupation'), - // // onChanged: (value) => controller - // // .occupation.value = value, - // // ), - // // TextFormField( - // // initialValue: controller - // // .responseIdEgyptBack != - // // null - // // ? controller.responseIdEgyptBack[ - // // 'occupation'] - // // ?.toString() - // // : controller.occupation.value, - // // decoration: const InputDecoration( - // // labelText: 'Education'), - // // onChanged: (value) => controller - // // .education.value = value, - // // ), - // // TextFormField( - // // initialValue: controller - // // .responseIdEgyptBack != - // // null - // // ? controller.responseIdEgyptBack[ - // // 'issue_date'] - // // ?.toString() - // // : controller.issueDate.value, - // // decoration: const InputDecoration( - // // labelText: 'License Issue Date'), - // // onChanged: (value) => controller - // // .licenseIssueDate.value = value, - // // ), - // // TextFormField( - // // initialValue: controller - // // .responseIdEgyptBack != - // // null - // // ? controller.responseIdEgyptBack[ - // // 'religion'] - // // ?.toString() - // // : controller.religion.value, - // // decoration: const InputDecoration( - // // labelText: 'Religion'), - // // onChanged: (value) => - // // controller.religion.value = value, - // // ), - // // TextFormField( - // // initialValue: controller - // // .responseIdEgyptBack != - // // null - // // ? controller.responseIdEgyptBack[ - // // 'maritalStatus'] - // // ?.toString() - // // : controller.maritalStatus.value, - // // decoration: const InputDecoration( - // // labelText: 'Marital Status'), - // // onChanged: (value) => controller - // // .maritalStatus.value = value, - // // ), - // // TextFormField( - // // initialValue: - // // controller.responseIdEgyptDriverLicense != - // // null - // // ? controller - // // .responseIdEgyptDriverLicense[ - // // 'address'] - // // ?.toString() - // // : controller.address.value, - // // decoration: const InputDecoration( - // // labelText: 'Site'), - // // onChanged: (value) => - // // controller.site.value = value, - // // ), - // // TextFormField( - // // initialValue: - // // controller.responseIdEgyptDriverLicense != - // // null - // // ? controller - // // .responseIdEgyptDriverLicense[ - // // 'employmentType'] - // // ?.toString() - // // : controller - // // .employmentType.value, - // // decoration: const InputDecoration( - // // labelText: 'Employment Type'), - // // onChanged: (value) => controller - // // .employmentType.value = value, - // // ), - // // const Divider( - // // thickness: 5, - // // height: 5, - // // ), - // // Text('Car Details'.tr), - // // const Divider( - // // thickness: 5, - // // height: 5, - // // ), - // // TextFormField( - // // initialValue: - // // controller.responseIdCardDriverEgyptBack != - // // null - // // ? controller - // // .responseIdCardDriverEgyptBack[ - // // 'chassis'] - // // ?.toString() - // // : controller.vin.value, - // // decoration: const InputDecoration( - // // labelText: 'VIN'), - // // onChanged: (value) => - // // controller.vin.value = value, - // // ), - // // TextFormField( - // // initialValue: - // // controller.responseIdCardDriverEgyptBack != - // // null - // // ? controller - // // .responseIdCardDriverEgyptBack[ - // // 'make'] - // // ?.toString() - // // : controller.make.value, - // // decoration: const InputDecoration( - // // labelText: 'Make'), - // // onChanged: (value) => - // // controller.make.value = value, - // // ), - // // TextFormField( - // // initialValue: - // // controller.responseIdCardDriverEgyptBack != - // // null - // // ? controller - // // .responseIdCardDriverEgyptBack[ - // // 'model'] - // // ?.toString() - // // : controller.model.value, - // // decoration: const InputDecoration( - // // labelText: 'Model'), - // // onChanged: (value) => - // // controller.model.value = value, - // // ), - // // TextFormField( - // // initialValue: - // // controller.responseIdCardDriverEgyptBack != - // // null - // // ? controller - // // .responseIdCardDriverEgyptBack[ - // // 'year'] - // // ?.toString() - // // : controller.year.value, - // // decoration: const InputDecoration( - // // labelText: 'Year'), - // // onChanged: (value) => - // // controller.year.value = value, - // // ), - // // TextFormField( - // // initialValue: - // // controller.responseIdCardDriverEgyptBack != - // // null - // // ? controller - // // .responseIdCardDriverEgyptBack[ - // // 'engine'] - // // ?.toString() - // // : controller - // // .displacement.value, - // // decoration: const InputDecoration( - // // labelText: 'Displacement'), - // // onChanged: (value) => controller - // // .displacement.value = value, - // // ), - // // TextFormField( - // // initialValue: - // // controller.responseIdCardDriverEgyptBack != - // // null - // // ? controller - // // .responseIdCardDriverEgyptBack[ - // // 'fuel'] - // // ?.toString() - // // : controller.fuel.value, - // // decoration: const InputDecoration( - // // labelText: 'Fuel'), - // // onChanged: (value) => - // // controller.fuel.value = value, - // // ), - // // DropdownButtonFormField( - // // decoration: InputDecoration( - // // labelText: - // // 'Color'.tr, // Localized label - // // ), - // // value: controller.color.value.isEmpty - // // ? null - // // : controller.color.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( - // // value: colorValue, - // // child: Text(colorName), - // // ); - // // }).toList(), - // // onChanged: (value) { - // // controller.color.value = - // // value ?? ''; - // // controller.colorHex.value = - // // value ?? ''; - // // }, - // // ), - // // TextFormField( - // // initialValue: - // // controller.responseIdCardDriverEgyptFront != - // // null - // // ? controller - // // .responseIdCardDriverEgyptFront[ - // // 'owner'] - // // ?.toString() - // // : controller.owner.value, - // // decoration: const InputDecoration( - // // labelText: 'Owner'), - // // onChanged: (value) => - // // controller.owner.value = value, - // // ), - // // TextFormField( - // // initialValue: - // // controller.responseIdCardDriverEgyptFront != - // // null - // // ? controller - // // .responseIdCardDriverEgyptFront[ - // // 'address'] - // // ?.toString() - // // : controller.addressCar.value, - // // decoration: const InputDecoration( - // // labelText: 'Address'), - // // onChanged: (value) => controller - // // .addressCar.value = value, - // // ), - // // TextFormField( - // // initialValue: - // // inspectionDateTime.toString(), - // // decoration: const InputDecoration( - // // labelText: 'Registration Date'), - // // onChanged: (value) => controller - // // .registrationDate.value = value, - // // ), - // // const SizedBox(height: 20), - // // ], - // // ), - // // ), - // // ), - // // confirm: MyElevatedButton( - // // title: 'Upload'.tr, - // // onPressed: () async { - // // await controller.addDriverEgyptHanding(); - // // await controller - // // .addRegistrationCarEgyptHandling(); - // // await controller - // // .getDriverNotCompleteRegistration(); - // // Get.back(); - // // }, - // // kolor: AppColor.greenColor, - // // ), - // // cancel: MyElevatedButton( - // // kolor: AppColor.redColor, - // // title: 'Cancel'.tr, - // // onPressed: () { - // // Get.back(); - // // })); - // }, - // child: Text( - // "hand inserting".tr, - // ), - // ); - // } + Widget _buildNavigationControls(RegisterCaptainController controller) { + bool isLastPage = controller.currentPageIndex.value == 3; + bool isFirstPage = controller.currentPageIndex.value == 0; - TextButton handleInsert(RegisterCaptainController controller) { - final now = DateTime.now(); - final formattedDate = DateFormat('yyyy-MM-dd').format(now); - return TextButton( - onPressed: () { - Get.to(MyScaffold( - title: 'title', - isleading: true, - body: [ - Column( - children: [ - Expanded( - // Use Expanded to make the Form scrollable if needed - child: Form( - child: ListView( - padding: const EdgeInsets.all(16.0), - children: [ - //------------------------ Personal info --------------------------- - Text('Personal Information'.tr, - style: TextStyle( - fontSize: 18, fontWeight: FontWeight.bold)), - const Divider(thickness: 1), - TextFormField( - initialValue: controller - .responseIdEgyptDriverLicense?['firstName'] - ?.toString() ?? - controller.firstName.value, - decoration: - const InputDecoration(labelText: 'First Name'), - onChanged: (value) => - controller.firstName.value = value, - ), - TextFormField( - initialValue: controller - .responseIdEgyptDriverLicense?['lastName'] - ?.toString() ?? - controller.lastName.value, - decoration: - const InputDecoration(labelText: 'Last Name'), - onChanged: (value) => - controller.lastName.value = value, - ), - TextFormField( - initialValue: controller - .responseIdEgyptBack?['gender'] - ?.toString() ?? - controller.gender.value, - decoration: - const InputDecoration(labelText: 'Gender'), - onChanged: (value) => controller.gender.value = value, - ), - TextFormField( - initialValue: controller - .responseIdEgyptDriverLicense?['license_type'] - ?.toString() ?? - controller.licenseType.value, - decoration: - const InputDecoration(labelText: 'License Type'), - onChanged: (value) => - controller.licenseType.value = value, - ), - TextFormField( - initialValue: controller - .responseIdEgyptDriverLicense?[ - 'national_number'] - ?.toString() ?? - controller.nationalNumber.value, - decoration: const InputDecoration( - labelText: 'National Number'), - onChanged: (value) => - controller.nationalNumber.value = value, - ), - TextFormField( - initialValue: controller - .responseIdEgyptDriverLicense?['name_arabic'] - ?.toString() ?? - controller.nameArabic.value, - decoration: const InputDecoration( - labelText: 'Name in Arabic'), - onChanged: (value) => - controller.nameArabic.value = value, - ), - TextFormField( - initialValue: controller - .responseIdEgyptDriverLicense?['name_english'] - ?.toString() ?? - controller.nameEnglish.value, - decoration: const InputDecoration( - labelText: 'Name in English'), - onChanged: (value) => - controller.nameEnglish.value = value, - ), - TextFormField( - initialValue: controller - .responseIdEgyptDriverLicense?['issue_date'] - ?.toString() ?? - controller.issueDate.value, - decoration: - const InputDecoration(labelText: 'Issue Date'), - onChanged: (value) => - controller.issueDate.value = value, - ), - TextFormField( - initialValue: controller - .responseIdEgyptDriverLicense?['expiry_date'] - ?.toString() ?? - controller.expirationDate.value, - decoration: - const InputDecoration(labelText: 'Expiry Date'), - onChanged: (value) => - controller.expiryDate.value = value, - ), - TextFormField( - initialValue: - controller.responseIdEgyptDriverLicense?[ - 'license_categories'] is List - ? (controller.responseIdEgyptDriverLicense?[ - 'license_categories'] as List) - .join(', ') - : controller.responseIdEgyptDriverLicense?[ - 'license_categories'] - ?.toString() ?? - controller.licenseCategories.value, - decoration: const InputDecoration( - labelText: 'License Categories'), - onChanged: (value) => - controller.licenseCategories.value = value, - ), - TextFormField( - initialValue: controller - .responseIdEgyptFront?['address'] - ?.toString() ?? - controller.address.value, - decoration: - const InputDecoration(labelText: 'Address'), - onChanged: (value) => - controller.address.value = value, - ), - TextFormField( - initialValue: controller - .responseIdEgyptFront?['card_id'] - ?.toString() ?? - controller.cardId.value, - decoration: - const InputDecoration(labelText: 'Card ID'), - onChanged: (value) => controller.cardId.value = value, - ), - TextFormField( - initialValue: controller - .responseIdEgyptBack?['occupation'] - ?.toString() ?? - controller.occupation.value, - decoration: - const InputDecoration(labelText: 'Occupation'), - onChanged: (value) => - controller.occupation.value = value, - ), - TextFormField( - initialValue: controller - .responseIdEgyptBack?['occupation'] - ?.toString() ?? - controller.education.value, - decoration: - const InputDecoration(labelText: 'Education'), - onChanged: (value) => - controller.education.value = value, - ), - TextFormField( - initialValue: controller - .responseIdEgyptBack?['issue_date'] - ?.toString() ?? - controller.licenseIssueDate.value, - decoration: const InputDecoration( - labelText: 'License Issue Date'), - onChanged: (value) => - controller.licenseIssueDate.value = value, - ), - TextFormField( - initialValue: controller - .responseIdEgyptBack?['religion'] - ?.toString() ?? - controller.religion.value, - decoration: - const InputDecoration(labelText: 'Religion'), - onChanged: (value) => - controller.religion.value = value, - ), - TextFormField( - initialValue: controller - .responseIdEgyptBack?['maritalStatus'] - ?.toString() ?? - controller.maritalStatus.value, - decoration: const InputDecoration( - labelText: 'Marital Status'), - onChanged: (value) => - controller.maritalStatus.value = value, - ), - TextFormField( - initialValue: controller - .responseIdEgyptDriverLicense?['address'] - ?.toString() ?? - controller.site.value, - decoration: const InputDecoration(labelText: 'Site'), - onChanged: (value) => controller.site.value = value, - ), - TextFormField( - initialValue: controller - .responseIdEgyptDriverLicense?[ - 'employmentType'] - ?.toString() ?? - controller.employmentType.value, - decoration: const InputDecoration( - labelText: 'Employment Type'), - onChanged: (value) => - controller.employmentType.value = value, - ), + 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, + ), + ], + ), + ); + } - //------------------------ Car Details --------------------------- + 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), + ), + ), + ), + ); + } - const SizedBox(height: 20), - Text('Car Details'.tr, - style: TextStyle( - fontSize: 18, fontWeight: FontWeight.bold)), - const Divider(thickness: 1), - - TextFormField( - initialValue: controller.vin.value.isEmpty - ? controller - .responseIdCardDriverEgyptBack['chassis'] - ?.toString() ?? - '' - : controller.vin.value, - decoration: const InputDecoration(labelText: 'VIN'), - onChanged: (value) => controller.vin.value = value, - ), - TextFormField( - initialValue: controller.vin.value.isEmpty - ? controller.responseIdCardDriverEgyptBack[ - 'car_plate'] - ?.toString() ?? - '' - : controller.vin.value, - decoration: - const InputDecoration(labelText: 'Car Plate'), - onChanged: (value) => controller.carPlate.value = - value, // changed to carPlate - ), - TextFormField( - initialValue: controller.make.value.isEmpty - ? controller.responseIdCardDriverEgyptBack['make'] - ?.toString() ?? - '' - : controller.make.value, - decoration: const InputDecoration(labelText: 'Make'), - onChanged: (value) => controller.make.value = value, - ), - TextFormField( - initialValue: controller.model.value.isEmpty - ? controller - .responseIdCardDriverEgyptBack['model'] - ?.toString() ?? - '' - : controller.model.value, - decoration: const InputDecoration(labelText: 'Model'), - onChanged: (value) => controller.model.value = value, - ), - TextFormField( - initialValue: controller.year.value.isEmpty - ? controller.responseIdCardDriverEgyptBack['year'] - ?.toString() ?? - '' - : controller.year.value, - decoration: const InputDecoration(labelText: 'Year'), - onChanged: (value) => controller.year.value = value, - ), - TextFormField( - initialValue: controller.displacement.value.isEmpty - ? controller - .responseIdCardDriverEgyptBack['engine'] - ?.toString() ?? - '' - : controller.displacement.value, - decoration: - const InputDecoration(labelText: 'Displacement'), - onChanged: (value) => - controller.displacement.value = value, - ), - TextFormField( - initialValue: controller.fuel.value.isEmpty - ? controller.responseIdCardDriverEgyptBack['fuel'] - ?.toString() ?? - '' - : controller.fuel.value, - decoration: const InputDecoration(labelText: 'Fuel'), - onChanged: (value) => controller.fuel.value = value, - ), - DropdownButtonFormField( - decoration: InputDecoration(labelText: 'Color'.tr), - value: controller.color.value.isEmpty - ? null - : controller.color.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( - value: colorValue, - child: Text(colorName), - ); - }).toList(), - onChanged: (value) { - controller.color.value = value ?? ''; - controller.colorHex.value = value ?? ''; - }, - ), - TextFormField( - initialValue: controller.owner.value.isEmpty - ? controller - .responseIdCardDriverEgyptFront['owner'] - ?.toString() ?? - '' - : controller.owner.value, - decoration: const InputDecoration(labelText: 'Owner'), - onChanged: (value) => controller.owner.value = value, - ), - TextFormField( - initialValue: controller.expirationDate.value.isEmpty - ? controller.responseIdCardDriverEgyptFront[ - 'expiration_date'] - ?.toString() ?? - '' - : controller.expirationDate.value, - decoration: const InputDecoration( - labelText: 'Expiration Date'), - onChanged: (value) => - controller.expirationDate.value = value, - ), - TextFormField( - initialValue: controller.addressCar.isEmpty - ? controller - .responseIdCardDriverEgyptFront['address'] - ?.toString() ?? - '' - : controller.addressCar.value, - decoration: - const InputDecoration(labelText: 'Address'), - onChanged: (value) => - controller.addressCar.value = value, - ), - TextFormField( - initialValue: - controller.registrationDate.value.isEmpty - ? formattedDate - : controller.registrationDate.value, - decoration: const InputDecoration( - labelText: 'Registration Date'), - onChanged: (value) => - controller.registrationDate.value = value, - ), - - const SizedBox(height: 20), - ], + Widget _buildPageContent({ + required RxString imageUrl, + required List 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), ), ), - ), - MyElevatedButton( - title: 'Upload'.tr, - onPressed: () async { - await controller.addDriverEgyptHanding(); - if (controller.responseIdCardDriverEgyptBack != null) { - await controller.addRegistrationCarEgyptHandling(); - } + ); + }, + ), + ), + const SizedBox(height: 20), + const Divider(), + ...formFields, + ], + ), + ); + } - Get.back(); - // await controller.getDriverNotCompleteRegistration(); - }, - kolor: AppColor.greenColor, - ), - ], + Widget _buildColorDropdown(RegisterCaptainController controller) { + return Padding( + padding: const EdgeInsets.symmetric(vertical: 8.0), + child: Obx( + () => DropdownButtonFormField( + 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( + 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( + 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( + 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( + 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( + 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), ), ], - )); - }, - child: Text("Hand Inserting".tr), + ), + _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)), + ], ); } - GetBuilder egyptDriverLicense() { - return GetBuilder( - builder: (ai) { - if (ai.responseIdEgyptDriverLicense.isNotEmpty) { - final expiryDate = ai.responseIdEgyptDriverLicense['expiry_date']; + // --- 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)), + ], + ); + } - // Check if the expiry date is before today - final today = DateTime.now(); - - // Try parsing the expiry date. If it fails, set it to null. - final expiryDateTime = DateTime.tryParse(expiryDate); - final isExpired = - expiryDateTime != null && expiryDateTime.isBefore(today); - - return Card( - elevation: 4.0, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(16.0), - ), - child: Padding( - padding: const EdgeInsets.all(16.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text('Driver\'s License'.tr, style: AppStyle.headTitle2), - IconButton( - onPressed: () async { - await ai.allMethodForAI(""" - -Here’s the updated version of your prompt with the necessary changes: - -{ -"license_type": "", -"national_number": "", -"name_arabic": "", -"name_english": "", -"firstName": "", -"lastName": "", -"address": "", -"issue_date": "", -"expiry_date": "", -"employmentType": "", -"license_categories": [] -} • Only include fields that are present in the input text. Use null for any missing values. -• Format dates as YYYY-MM-DD. -• For the national_number, ensure it is provided in Latin numerals and consists of exactly 14 digits. Remove any non-numeric characters or incorrect length and format it properly. -• For the name, split the name into first and last names based on the structure provided. - -The response should adhere to the specified format and include only the requested fields. -""", 'driver_license', ai.driverId); //egypt - }, - icon: const Icon(Icons.refresh), - ), - ], - ), - const SizedBox(height: 8.0), - const Divider(color: AppColor.accentColor), - const SizedBox(height: 8.0), - Text( - '${'License Type'.tr}: ${ai.responseIdEgyptDriverLicense['license_type']}', - style: AppStyle.title, - ), - const SizedBox(height: 8.0), - Text( - '${'National Number'.tr}: ${ai.responseIdEgyptDriverLicense['national_number']}', - style: AppStyle.title.copyWith( - color: ai.responseIdEgyptDriverLicense[ - 'national_number'] == - ai.responseIdEgyptBack['nationalID'] - ? AppColor.greenColor - : AppColor.redColor), - ), - const SizedBox(height: 8.0), - Text( - '${'Name (Arabic)'.tr}: ${ai.responseIdEgyptDriverLicense['name_arabic']}', - ), - const SizedBox(height: 8.0), - Text( - '${'Name (English)'.tr}: ${ai.responseIdEgyptDriverLicense['name_english']}', - ), - const SizedBox(height: 8.0), - Text( - '${'Address'.tr}: ${ai.responseIdEgyptDriverLicense['address']}', - ), - const SizedBox(height: 8.0), - Text( - '${'Issue Date'.tr}: ${ai.responseIdEgyptDriverLicense['issue_date']}', - ), - const SizedBox(height: 8.0), - Text( - '${'Expiry Date'.tr}: ${ai.responseIdEgyptDriverLicense['expiry_date']}', - style: AppStyle.title.copyWith( - color: - !isExpired ? AppColor.greenColor : AppColor.redColor, - ), - ), - const SizedBox(height: 8.0), - Text( - '${'License Categories'.tr}: ${ai.responseIdEgyptDriverLicense['license_categories']}', - ), - ], + // --- 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, ), ), - ); - } - return Card( - child: InkWell( - onTap: () async { - await ai.allMethodForAI(""" - -Here’s the updated version of your prompt with the necessary changes: - -{ -"license_type": "", -"national_number": "", -"name_arabic": "", -"name_english": "", -"firstName": "", -"lastName": "", -"address": "", -"issue_date": "", -"expiry_date": "", -"employmentType": "", -"license_categories": [] -} • Only include fields that are present in the input text. Use null for any missing values. -• Format dates as YYYY-MM-DD. -• For the national_number, ensure it is provided in Latin numerals and consists of exactly 14 digits. Remove any non-numeric characters or incorrect length and format it properly. -• For the name, split the name into first and last names based on the structure provided. - -The response should adhere to the specified format and include only the requested fields. -""", 'driver_license', ai.driverId); //egypt - }, - child: Column( - children: [ - Image.network( - '${AppLink.server}/card_image/driver_license-${ai.driverId}.jpg', - height: Get.height * .25, - width: double.maxFinite, - fit: BoxFit.fitHeight, - errorBuilder: (BuildContext context, Object exception, - StackTrace? stackTrace) { - return Text("Not found any image".tr); - }, - ), - Text( - 'Capture an Image of Your Driver License'.tr, - style: AppStyle.title, - ), - ], - ), - ), - ); - }, + ], + ), + // _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))), + ], + ), + ], ); } - GetBuilder egyptDriverIDBack() { - return GetBuilder( - builder: (ai) { - if (ai.responseIdEgyptBack.isNotEmpty) { - final taxExpiryDate = ai.responseIdEgyptBack['expirationDate']; + // --- 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)), + ], + ), + ], + ); + } +} - // Check if the tax expiry date is before today - final today = DateTime.now(); +class StepIndicator extends StatelessWidget { + final int currentStep; + final int totalSteps; - // Try parsing the tax expiry date. If it fails, set it to null. - final taxExpiryDateTime = DateTime.tryParse(taxExpiryDate); - final isExpired = - taxExpiryDateTime != null && taxExpiryDateTime.isBefore(today); + const StepIndicator({ + super.key, + required this.currentStep, + required this.totalSteps, + }); - return Card( - elevation: 4.0, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(16.0), - ), - child: Padding( - padding: const EdgeInsets.all(16.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text('ID Documents Back'.tr, style: AppStyle.headTitle2), - IconButton( - onPressed: () async { - await ai.allMethodForAI(""" - Given the following Arabic text values: - - -Please create a JSON object with the following fields: - - -nationalID: Use exactly 14 digits, converting from Arabic numerals to Latin numerals (0-9). -issueDate: Format as YYYY-MM-DD using Latin numerals, setting the date to the last day of the provided month. -occupation: Translate and format consistently (e.g., all lowercase). -gender: Use the values "Male" or "Female" without translation. -country: Set to "جمهورية مصر العربية". -religion: Translate and format consistently (e.g., all lowercase). -maritalStatus: Translate and format consistently (e.g., all lowercase). -fullNameMarital: If maritalStatus is "أعزب", set this to "none". Otherwise, leave it as "none". -expirationDate: Format as YYYY-MM-DD using Latin numerals. -Important notes: - - -Ensure all date fields use Latin (Western) numerals (0-9) instead of Arabic numerals. -For the nationalID, use exactly 14 digits from the provided Arabic numeral string, converting to Latin numerals. -For issueDate, use the last day of the month since only year and month are provided. -Include the country field as "جمهورية مصر العربية". -Format all string values consistently (e.g., all lowercase or proper case) - """, 'id_back', ai.driverId); //egypt - }, - icon: const Icon(Icons.refresh), - ), - ], - ), - const SizedBox(height: 8.0), - const Divider(color: AppColor.accentColor), - const SizedBox(height: 8.0), - // Assuming these keys exist in ai.responseIdEgyptFront - Text( - '${'National ID'.tr}: ${ai.responseIdEgyptBack['nationalID']}', - style: AppStyle.title.copyWith( - color: ai.responseIdEgyptDriverLicense[ - 'national_number'] == - ai.responseIdEgyptBack['nationalID'] - ? AppColor.greenColor - : AppColor.redColor), - ), - - const SizedBox(height: 8.0), - Text( - '${'Occupation'.tr}: ${ai.responseIdEgyptBack['occupation']}', // Assuming 'occupation' exists - ), - const SizedBox(height: 8.0), - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - '${'Issue Date'.tr}: ${ai.responseIdEgyptBack['issueDate']}', // Assuming 'issueDate' exists - ), - Text( - '${'Gender'.tr}: ${ai.responseIdEgyptBack['gender']}', // Assuming 'gender' exists - ), - ], - ), - const SizedBox(height: 8.0), - // Row( - // mainAxisAlignment: MainAxisAlignment.spaceBetween, - // children: [ - // Text( - // '${'Religion'.tr}: ${ai.responseIdEgyptBack['religion']}', // Assuming 'religion' exists - // ), - // Text( - // '${'Marital Status'.tr}: ${ai.responseIdEgyptBack['maritalStatus']}', // Assuming 'maritalStatus' exists - // ), - // ], - // ), - // const SizedBox(height: 8.0), - // Text( - // '${'Full Name (Marital)'.tr}: ${ai.responseIdEgyptBack['fullNameMaritial']}', // Assuming 'fullNameMaritial' exists - // ), - // const SizedBox(height: 8.0), - Text( - '${'Expiration Date'.tr}: ${ai.responseIdEgyptBack['expirationDate']}', // Assuming 'expirationDate' exists - style: AppStyle.title.copyWith( - color: !isExpired - ? AppColor.greenColor - : AppColor.redColor), - ), - ], + @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), ), - ), - ); - } - return Card( - child: InkWell( - onTap: () async { - await ai.allMethodForAI(''' - Given the following Arabic text values: - - -Please create a JSON object with the following fields: - - -nationalID: Use exactly 14 digits, converting from Arabic numerals to Latin numerals (0-9). -issueDate: Format as YYYY-MM-DD using Latin numerals, setting the date to the last day of the provided month. -occupation: Translate and format consistently (e.g., all lowercase). -gender: Use the values "Male" or "Female" without translation. -country: Set to "جمهورية مصر العربية". -religion: Translate and format consistently (e.g., all lowercase). -maritalStatus: Translate and format consistently (e.g., all lowercase). -fullNameMarital: If maritalStatus is "أعزب", set this to "none". Otherwise, leave it as "none". -expirationDate: Format as YYYY-MM-DD using Latin numerals. -Important notes: - - -Ensure all date fields use Latin (Western) numerals (0-9) instead of Arabic numerals. -For the nationalID, use exactly 14 digits from the provided Arabic numeral string, converting to Latin numerals. -For issueDate, use the last day of the month since only year and month are provided. -Include the country field as "جمهورية مصر العربية". -Format all string values consistently (e.g., all lowercase or proper case) - ''', 'id_back', ai.driverId); //egypt - }, - child: Column( - children: [ - Image.network( - '${AppLink.server}/card_image/id_back-${ai.driverId}.jpg', - height: Get.height * .25, - width: double.maxFinite, - fit: BoxFit.fitHeight, - errorBuilder: (BuildContext context, Object exception, - StackTrace? stackTrace) { - return Text("Not found any image".tr); - }, - ), - Text( - 'Capture an Image of Your ID Document Back'.tr, - style: AppStyle.title, - ), - ], - ), - ), - ); - }, - ); - } - - GetBuilder nonEgyptDriverIDBack() { - return GetBuilder( - builder: (ai) { - if (ai.responseIdEgyptBack.isNotEmpty) { - final taxExpiryDate = ai.responseIdEgyptBack['expirationDate']; - - // Check if the tax expiry date is before today - final today = DateTime.now(); - - // Try parsing the tax expiry date. If it fails, set it to null. - final taxExpiryDateTime = DateTime.tryParse(taxExpiryDate); - final isExpired = - taxExpiryDateTime != null && taxExpiryDateTime.isBefore(today); - - return Card( - elevation: 4.0, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(16.0), - ), - child: Padding( - padding: const EdgeInsets.all(16.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text('ID Documents Back'.tr, style: AppStyle.headTitle2), - IconButton( - onPressed: () async { - await ai.allMethodForAI(""" - Given an Egyptian residence card data: - -1. From the Arabic text sections: - - Extract residency type after "نوع الإقامة:" - - Extract work status (غير مصرح بالعمل or مصرح بالعمل) - -2. From the issue date line: - - Parse date after "IssueDate:" in YYYY-MM-DD format - -3. From MRZ line 2 (e.g., "9205200M2706258SYR"): - First part (before 'M'): - - If digit before 'M' is 2: year = "19" + first 2 digits - - If digit before 'M' is 3: year = "20" + first 2 digits - - Month = next 2 digits - - Day = last 2 digits - - After 'M': - - Year = "20" + first 2 digits - - Month = next 2 digits - - Day = last 2 digits - - Country code: - - Last 3 characters convert to Arabic country name - -Format as JSON with fields: -- issueDate -- birthDate -- residencyType -- workStatus -- country (in Arabic) -- residencyExpirationDate - """, 'id_back', ai.driverId); //egypt - }, - icon: const Icon(Icons.refresh), - ), - ], - ), - const SizedBox(height: 8.0), - const Divider(color: AppColor.accentColor), - const SizedBox(height: 8.0), - // Assuming these keys exist in ai.responseIdEgyptFront - Text( - '${'National ID'.tr}: ${ai.responseIdEgyptBack['nationalID']}', - style: AppStyle.title.copyWith( - color: ai.responseIdEgyptDriverLicense[ - 'national_number'] == - ai.responseIdEgyptBack['nationalID'] - ? AppColor.greenColor - : AppColor.redColor), - ), - - const SizedBox(height: 8.0), - Text( - '${'Occupation'.tr}: ${ai.responseIdEgyptBack['occupation']}', // Assuming 'occupation' exists - ), - const SizedBox(height: 8.0), - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - '${'Issue Date'.tr}: ${ai.responseIdEgyptBack['issueDate']}', // Assuming 'issueDate' exists - ), - Text( - '${'Gender'.tr}: ${ai.responseIdEgyptBack['gender']}', // Assuming 'gender' exists - ), - ], - ), - const SizedBox(height: 8.0), - // Row( - // mainAxisAlignment: MainAxisAlignment.spaceBetween, - // children: [ - // Text( - // '${'Religion'.tr}: ${ai.responseIdEgyptBack['religion']}', // Assuming 'religion' exists - // ), - // Text( - // '${'Marital Status'.tr}: ${ai.responseIdEgyptBack['maritalStatus']}', // Assuming 'maritalStatus' exists - // ), - // ], - // ), - // const SizedBox(height: 8.0), - // Text( - // '${'Full Name (Marital)'.tr}: ${ai.responseIdEgyptBack['fullNameMaritial']}', // Assuming 'fullNameMaritial' exists - // ), - // const SizedBox(height: 8.0), - Text( - '${'Expiration Date'.tr}: ${ai.responseIdEgyptBack['expirationDate']}', // Assuming 'expirationDate' exists - style: AppStyle.title.copyWith( - color: !isExpired - ? AppColor.greenColor - : AppColor.redColor), - ), - ], - ), - ), - ); - } - return Card( - child: InkWell( - onTap: () async { - await ai.allMethodForAI(''' - Given an Egyptian residence card data: - -1. From the Arabic text sections: - - Extract residency type after "نوع الإقامة:" - - Extract work status (غير مصرح بالعمل or مصرح بالعمل) - -2. From the issue date line: - - Parse date after "IssueDate:" in YYYY-MM-DD format - -3. From MRZ line 2 (e.g., "9205200M2706258SYR"): - First part (before 'M'): - - If digit before 'M' is 2: year = "19" + first 2 digits - - If digit before 'M' is 3: year = "20" + first 2 digits - - Month = next 2 digits - - Day = last 2 digits - - After 'M': - - Year = "20" + first 2 digits - - Month = next 2 digits - - Day = last 2 digits - - Country code: - - Last 3 characters convert to Arabic country name - -Format as JSON with fields: -- issueDate -- birthDate -- residencyType -- workStatus -- country (in Arabic) -- residencyExpirationDate ''', 'id_back', ai.driverId); //egypt - }, - child: Column( - children: [ - Image.network( - '${AppLink.server}/card_image/id_back-${ai.driverId}.jpg', - height: Get.height * .25, - width: double.maxFinite, - fit: BoxFit.fitHeight, - errorBuilder: (BuildContext context, Object exception, - StackTrace? stackTrace) { - return Text("Not found any image".tr); - }, - ), - Text( - 'Capture an Image of Your ID Document Back'.tr, - style: AppStyle.title, - ), - ], - ), - ), - ); - }, - ); - } - - GetBuilder nonEgyptDriverIDFront() { - return GetBuilder( - builder: (ai) { - if (ai.responseNonIdCardFront.isNotEmpty) { - return Card( - elevation: 4.0, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(16.0), - ), - child: Padding( - padding: const EdgeInsets.all(16.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text('Non-Egyptian ID Front'.tr, style: AppStyle.title), - IconButton( - onPressed: () async { - await ai.allMethodForAI(""" - Given the following Arabic text values: - -1. The full name. -2. The passport number, preceded by "جواز رقم". -3. The country, located immediately after the passport number. -4. The birth date, formatted in Arabic numerals as YYYY/MM/DD. -5. The gender, written in Arabic ("ذكر" for "Male" and "أنثى" for "Female"). -6. The address, located after "العنوان". -7. The card ID, found at the bottom right of the document. - -Create a structured JSON object with the following fields: -- full_name: The full name, directly as written in Arabic. -- passport_no: Extract the passport number. -- country: Extract the country following the passport number. -- birthdate: Format the birth date as YYYY-MM-DD using Western numerals (0-9). -- gender: Convert "ذكر" to "Male" and "أنثى" to "Female". -- address: Extract the full address as written. -- card_id: Extract the ID from the bottom right. - -Ensure the JSON is properly formatted and all values are accurately mapped. -{ - "first_name": "", // The word next to "بطاقة تحقيق الشخصية" (National Identification Card) - "full_name": "", // The full name on the next line after the first name - "address": "", // The complete address spanning the next two lines - "national_number": "", // The National ID number before the last line (convert Arabic numerals to Latin) - "card_id": "", // The card ID in English on the last line - "dob": "" // Year of birth only, in Latin numerals (YYYY format) -} - -Important notes: -1. For 'first_name', extract the word immediately following "بطاقة تحقيق الشخصية". -2. 'full_name' should be the complete name found on the line after the first name. -3. 'address' should combine information from two consecutive lines. -4. Convert the 'national_number' from Arabic numerals to Latin numerals (0-9). -5. 'card_id' should be extracted as-is from the last line (it's already in English). -6. For 'dob', include only the year of birth in YYYY format using Latin numerals. -7. If any information is missing, leave the field as an empty string. - """, AppLink.uploadEgypt, 'non_id_front'); - }, - icon: const Icon(Icons.refresh), - ), - ], - ), - const SizedBox(height: 8.0), - const Divider(color: AppColor.accentColor), - const SizedBox(height: 8.0), - Text( - '${'Full Name'.tr}: ${ai.responseNonIdCardFront['full_name']}', - style: AppStyle.title, - ), - const SizedBox(height: 8.0), - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - '${'Passport No'.tr}: ${ai.responseNonIdCardFront['passport_no']}', - ), - Text( - '${'Card ID'.tr}: ${ai.responseNonIdCardFront['card_id']}', - ), - ], - ), - const SizedBox(height: 8.0), - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - '${'Country'.tr}: ${ai.responseNonIdCardFront['country']}', - ), - Text( - '${'Gender'.tr}: ${ai.responseNonIdCardFront['gender']}', - ), - ], - ), - const SizedBox(height: 8.0), - Text( - '${'Birth Date'.tr}: ${ai.responseNonIdCardFront['birthdate']}', - ), - const SizedBox(height: 8.0), - Text( - '${'Address'.tr}: ${ai.responseNonIdCardFront['address']}', - ), - ], - ), - ), - ); - } - return Card( - child: InkWell( - onTap: () async { - await ai.allMethodForAI(""" -Given the following Arabic text values: - -1. The full name. -2. The passport number, preceded by "جواز رقم". -3. The country, located immediately after the passport number. -4. The birth date, formatted in Arabic numerals as YYYY/MM/DD. -5. The gender, written in Arabic ("ذكر" for "Male" and "أنثى" for "Female"). -6. The address, located after "العنوان". -7. The card ID, found at the bottom right of the document. - -Create a structured JSON object with the following fields: -- full_name: The full name, directly as written in Arabic. -- passport_no: Extract the passport number. -- country: Extract the country following the passport number. -- birthdate: Format the birth date as YYYY-MM-DD using Western numerals (0-9). -- gender: Convert "ذكر" to "Male" and "أنثى" to "Female". -- address: Extract the full address as written. -- card_id: Extract the ID from the bottom right. - -Ensure the JSON is properly formatted and all values are accurately mapped. -{ - "first_name": "", // The word next to "بطاقة تحقيق الشخصية" (National Identification Card) - "full_name": "", // The full name on the next line after the first name - "address": "", // The complete address spanning the next two lines - "national_number": "", // The National ID number before the last line (convert Arabic numerals to Latin) - "card_id": "", // The card ID in English on the last line - "dob": "" // Year of birth only, in Latin numerals (YYYY format) -} - -Important notes: -1. For 'first_name', extract the word immediately following "بطاقة تحقيق الشخصية". -2. 'full_name' should be the complete name found on the line after the first name. -3. 'address' should combine information from two consecutive lines. -4. Convert the 'national_number' from Arabic numerals to Latin numerals (0-9). -5. 'card_id' should be extracted as-is from the last line (it's already in English). -6. For 'dob', include only the year of birth in YYYY format using Latin numerals. -7. If any information is missing, leave the field as an empty string. -""", AppLink.uploadEgypt, 'non_id_front'); - }, - child: Column( - children: [ - Image.asset( - 'assets/images/7.jpeg', - height: Get.height * .25, - width: double.maxFinite, - fit: BoxFit.fitHeight, - ), - Text( - 'Capture Image of Non-Egyptian ID Front'.tr, - style: AppStyle.title, - ), - ], - ), - ), - ); - }, - ); - } - - GetBuilder egyptDriverIDFront() { - return GetBuilder( - builder: (ai) { - if (ai.responseIdEgyptFront.isNotEmpty) { - return Card( - elevation: 4.0, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(16.0), - ), - child: Padding( - padding: const EdgeInsets.all(16.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text('ID Documents Front'.tr, style: AppStyle.headTitle2), - IconButton( - onPressed: () async { - await ai.allMethodForAI(''' - Write a JSON object from the following information extracted from the provided Arabic text: - -{ - "first_name": "", // The word next to "بطاقة تحقيق الشخصية" (National Identification Card) - "full_name": "", // The full name on the next line after the first name - "address": "", // The complete address spanning the next two lines - "national_number": "", // The National ID number before the last line (convert Arabic numerals to Latin) - "card_id": "", // The card ID in English on the last line - "dob": "" // Year of birth only, in Latin numerals (YYYY format) -} - -Important notes: -1. For 'first_name', extract the word immediately following "بطاقة تحقيق الشخصية". -2. 'full_name' should be the complete name found on the line after the first name. -3. 'address' should combine information from two consecutive lines. -4. Convert the 'national_number' from Arabic numerals to Latin numerals (0-9). -5. 'card_id' should be extracted as-is from the last line (it's already in English). -6. For 'dob', include only the year of birth in YYYY format using Latin numerals. -7. If any information is missing, leave the field as an empty string. - ''', 'id_front', ai.driverId); //egypt - }, - icon: const Icon(Icons.refresh), - ), - ], - ), - const SizedBox(height: 8.0), - const Divider(color: AppColor.accentColor), - const SizedBox(height: 8.0), - // Removed Make, Model, etc. as they are not available - - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - '${'First Name'.tr}: ${ai.responseIdEgyptFront['first_name']}', - ), - Text( - '${'CardID'.tr}: ${ai.responseIdEgyptFront['card_id']}', - ), - ], - ), - const SizedBox(height: 8.0), - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - '${'Full Name'.tr}: ${ai.responseIdEgyptFront['full_name']}', - ), - Text( - '${'DOB'.tr}: ${ai.responseIdEgyptFront['dob']}', - ), - ], - ), - const SizedBox(height: 8.0), - Text( - '${'Address'.tr}: ${ai.responseIdEgyptFront['address']}', - ), - const SizedBox(height: 8.0), - // Text( - // '${'National Number'.tr}: ${ai.responseIdEgyptFront['national_number']}', - // ), - // const SizedBox(height: 8.0), - - // Removed Inspection Date as it's not available - ], - ), - ), - ); - } - return Card( - child: InkWell( - onTap: () async { - await ai.allMethodForAI("""" - Write a JSON object from the following information extracted from the provided Arabic text: - -{ - "first_name": "", // The word next to "بطاقة تحقيق الشخصية" (National Identification Card) - "full_name": "", // The full name on the next line after the first name - "address": "", // The complete address spanning the next two lines - "national_number": "", // The National ID number before the last line (convert Arabic numerals to Latin) - "card_id": "", // The card ID in English on the last line - "dob": "" // Year of birth only, in Latin numerals (YYYY format) -} - -Important notes: -1. For 'first_name', extract the word immediately following "بطاقة تحقيق الشخصية". -2. 'full_name' should be the complete name found on the line after the first name. -3. 'address' should combine information from two consecutive lines. -4. Convert the 'national_number' from Arabic numerals to Latin numerals (0-9). -5. 'card_id' should be extracted as-is from the last line (it's already in English). -6. For 'dob', include only the year of birth in YYYY format using Latin numerals. -7. If any information is missing, leave the field as an empty string. - """, 'id_front', ai.driverId); //egypt - }, - child: Column( - children: [ - Image.network( - //api.sefer.live/sefer/card_image/id_front-110822466514385876961.jpg - '${AppLink.server}/card_image/id_front-${ai.driverId}.jpg', - height: Get.height * .25, - width: double.maxFinite, - fit: BoxFit.fitHeight, - errorBuilder: (BuildContext context, Object exception, - StackTrace? stackTrace) { - return Text("Not found any image".tr); - }, - ), - Text( - 'Capture an Image of Your ID Document front'.tr, - style: AppStyle.title, - ), - ], - ), - ), - ); - }, - ); - } - - GetBuilder egyptCarLicenceFront() { - return GetBuilder( - builder: (ai) { - if (ai.responseIdCardDriverEgyptFront.isNotEmpty) { - // No need to access ai.responseIdCardDriverEgyptBack anymore - final licenseExpiryDate = DateTime.parse( - ai.responseIdCardDriverEgyptFront['LicenseExpirationDate']); - - // Check if license has expired - final today = DateTime.now(); - final isLicenseExpired = licenseExpiryDate.isBefore(today); - - return Card( - elevation: 4.0, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(16.0), - ), - child: Padding( - padding: const EdgeInsets.all(16.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - children: [ - Text('Vehicle Details Front'.tr, - style: AppStyle.headTitle2), - IconButton( - onPressed: () async { - ai.allMethodForAI(""" - Extract the following details from the provided car license data and format them into a JSON object: - - -License Expiration Date -Car Plate -Owner -Address - -Car License Data: - - -JSON Format: -{ -"LicenseExpirationDate": "YYYY-MM-DD", -"car_plate": "[Car plate number]",//the car plate is line next to line contain 'ادارة مرور' for bot numbers and letters in arabic with partition like| but you remove | -"owner": "[Owner's full name]", -"address": "[Address if available, otherwise 'Not provided']" -} - -Important notes: -1. For the LicenseExpirationDate, ensure the date is in YYYY-MM-DD format using Latin numerals (0-9). -2. Replace all occurrences of '|' (pipe character) with a space in all fields. -3. If any information is missing, leave the corresponding field as an empty string. -4. Ensure all text is properly formatted and spaces are used correctly. - -Please fill in the JSON object with the extracted information, following these guidelines. -""", 'car_front', ai.driverId); - }, - icon: const Icon(Icons.refresh), - ), - ], - ), - const SizedBox(height: 8.0), - const Divider(color: AppColor.accentColor), - const SizedBox(height: 8.0), - // Removed Make, Model, etc. as they are not available - - Text( - '${'Plate Number'.tr}: ${ai.responseIdCardDriverEgyptFront['car_plate']}', - ), - const SizedBox(height: 8.0), - Text( - '${'Owner Name'.tr}: ${ai.responseIdCardDriverEgyptFront['owner']}', - ), - const SizedBox(height: 8.0), - Text( - '${'Address'.tr}: ${ai.responseIdCardDriverEgyptFront['address']}', - ), - const SizedBox(height: 8.0), - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - '${'License Expiry Date'.tr}: ${licenseExpiryDate.toString().substring(0, 10)}', - style: TextStyle( - color: isLicenseExpired ? Colors.red : Colors.green, - ), - ), - // Removed Fuel as it's not available - ], - ), - // Removed Inspection Date as it's not available - ], - ), - ), - ); - } - return Card( - child: InkWell( - onTap: () async { - ai.allMethodForAI(""" - Extract the following details from the provided car license data and format them into a JSON object: - - -License Expiration Date -Car Plate -Owner -Address - -Car License Data: - - -JSON Format: -{ -"LicenseExpirationDate": "YYYY-MM-DD", -"car_plate": "[Car plate number]",//the car plate is line next to line contain 'ادارة مرور' for bot numbers and letters in arabic with partition like| but you remove | -"owner": "[Owner's full name]", -"address": "[Address if available, otherwise 'Not provided']" -} - -Important notes: -1. For the LicenseExpirationDate, ensure the date is in YYYY-MM-DD format using Latin numerals (0-9). -2. Replace all occurrences of '|' (pipe character) with a space in all fields. -3. If any information is missing, leave the corresponding field as an empty string. -4. Ensure all text is properly formatted and spaces are used correctly. - -Please fill in the JSON object with the extracted information, following these guidelines. -""", 'car_front', ai.driverId); - }, - child: Column( - children: [ - Image.network( - '${AppLink.server}/card_image/car_front-${ai.driverId}.jpg', - height: Get.height * .25, - width: double.maxFinite, - fit: BoxFit.fitHeight, - errorBuilder: (BuildContext context, Object exception, - StackTrace? stackTrace) { - return Text("Not found any image".tr); - }, - ), - Text( - 'Capture an Image of Your car license front'.tr, - style: AppStyle.title, - ), - ], - ), - ), - ); - }, - ); - } - - GetBuilder egyptCarLicenceBack() { - return GetBuilder( - builder: (ai) { - if (ai.responseIdCardDriverEgyptBack.isNotEmpty) { - // Get the tax expiry date from the response - final taxExpiryDate = ai.responseIdCardDriverEgyptBack['tax_expiry']; - // final displacement = ai.responseIdCardDriverEgyptBack['displacement']; - // if (int.parse(displacement) < 1000) {} - // Get the inspection date from the response - final inspectionDate = - ai.responseIdCardDriverEgyptBack['inspection_date']; - final year = int.parse(inspectionDate.split('-')[0]); - -// Set inspectionDateTime to December 31st of the given year - final inspectionDateTime = DateTime(year, 12, 31); - String carBackLicenseExpired = - inspectionDateTime.toString().split(' ')[0]; -// Get the current date - final today = DateTime.now(); - -// Try parsing the tax expiry date. If it fails, set it to null. - final taxExpiryDateTime = DateTime.tryParse(taxExpiryDate ?? ''); - final isExpired = - taxExpiryDateTime != null && taxExpiryDateTime.isBefore(today); -// Check if the inspection date is before today - bool isInspectionExpired = inspectionDateTime.isBefore(today); - - return Card( - elevation: 4.0, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(16.0), - ), - child: Padding( - padding: const EdgeInsets.all(16.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text('Vehicle Details Back'.tr, - style: AppStyle.headTitle2), - IconButton( - onPressed: () async { - ai.allMethodForAI(""" -Analyze the extracted car license information and create a JSON object with the following keys: - -{ - "make": "", - "year": "", - "chassis": "", - "model": "", - "engine": "", - "displacement": "", - "cylinders": "", - "fuel": "", - "color": "", - "color_hex": "", - "inspection_date": "", - "assuranceNumber": "", - "tax_expiry": "" -} - -Important notes: -1. For dates (inspection_date and tax_expiry), use the format YYYY-MM-DD with Latin numerals (0-9). -2. Convert the color name to its corresponding hex color code for the 'color_hex' field. -3. Ensure all numeric values (year, displacement, cylinders) are in Latin numerals. -4. If any information is missing, leave the corresponding field as an empty string. -5. Do not include any explanatory text in the JSON fields, only the extracted values. -displacement in the line contain (سم٣ ) -Please fill in the JSON object with the extracted information, following these guidelines. -""", 'car_back', ai.driverId); - }, - icon: const Icon(Icons.refresh), - ), - ], - ), - const SizedBox(height: 8.0), - const Divider(color: AppColor.accentColor), - const SizedBox(height: 8.0), - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - '${'Make'.tr}: ${ai.responseIdCardDriverEgyptBack['make']}'), - Text( - '${'Model'.tr}: ${ai.responseIdCardDriverEgyptBack['model']}'), - ], - ), - const SizedBox(height: 8.0), - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - '${'Year'.tr}: ${ai.responseIdCardDriverEgyptBack['year']}'), - Text( - '${'Chassis'.tr}: ${ai.responseIdCardDriverEgyptBack['chassis']}'), - ], - ), - const SizedBox(height: 8.0), - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - '${'Color'.tr}: ${ai.responseIdCardDriverEgyptBack['color']}'), - Text( - '${'Displacement'.tr}: ${ai.responseIdCardDriverEgyptBack['displacement']} cc'), - ], - ), - const SizedBox(height: 8.0), - Text( - '${'Fuel'.tr}: ${ai.responseIdCardDriverEgyptBack['fuel']}'), - const SizedBox(height: 8.0), - if (taxExpiryDateTime != null) - Text( - '${'Tax Expiry Date'.tr}: $taxExpiryDate', - style: TextStyle( - color: isExpired ? Colors.red : Colors.green, - ), - ), - const SizedBox(height: 8.0), - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - '${'Inspection Date'.tr}: $carBackLicenseExpired', - style: TextStyle( - color: - isInspectionExpired ? Colors.red : Colors.green, - ), - ), - ], - ), - ], - ), - ), - ); - } - return Card( - child: InkWell( - onTap: () async { - ai.allMethodForAI(""" -Analyze the extracted car license information and create a JSON object with the following keys: - -{ - "make": "", - "year": "", - "chassis": "", - "model": "", - "engine": "", - "displacement": "", - "cylinders": "", - "fuel": "", - "color": "", - "color_hex": "", - "inspection_date": "", - "assuranceNumber": "", - "tax_expiry": "" -} - -Important notes: -1. For dates (inspection_date and tax_expiry), use the format YYYY-MM-DD with Latin numerals (0-9). -2. Convert the color name to its corresponding hex color code for the 'color_hex' field. -3. Ensure all numeric values (year, displacement, cylinders) are in Latin numerals. -4. If any information is missing, leave the corresponding field as an empty string. -5. Do not include any explanatory text in the JSON fields, only the extracted values. - -Please fill in the JSON object with the extracted information, following these guidelines. -""", 'car_back', ai.driverId); - }, - child: Column( - children: [ - Image.network( - '${AppLink.server}/card_image/car_back-${ai.driverId}.jpg', - height: Get.height * .25, - width: double.maxFinite, - fit: BoxFit.fitHeight, - errorBuilder: (BuildContext context, Object exception, - StackTrace? stackTrace) { - return Text("Not found any image".tr); - }, - ), - Text( - 'Capture an Image of Your car license back'.tr, - style: AppStyle.title, - ), - ], - ), - ), - ); - }, - ); - } - - GetBuilder egyptCriminalRecord() { - return GetBuilder( - builder: (ai) { - if (ai.responseCriminalRecordEgypt.isNotEmpty) { - return Card( - elevation: 4.0, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(16.0), - ), - child: Padding( - padding: const EdgeInsets.all(16.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text('Criminal Record'.tr, style: AppStyle.headTitle2), - IconButton( - onPressed: () async { - await ai.allMethodForAI(""" -Write a JSON object from the following information extracted from the provided Arabic text: - -{ - "InspectionResult": "", - "NationalID": "", - "FullName": "", - "IssueDate": "" // Format: YYYY-MM-DD -} - -Important notes: -1. For the IssueDate, ensure the date is in YYYY-MM-DD format using Latin numerals (0-9). -2. Add appropriate spaces in all text fields to ensure readability. -3. If any information is missing, leave the corresponding field as an empty string. -4. Ensure all text is properly formatted and spaces are used correctly. -5. Convert any Arabic numerals to Latin numerals (0-9) where applicable. - -Please fill in the JSON object with the extracted information, following these guidelines. -""", 'criminalRecord', ai.driverId); - }, - icon: const Icon(Icons.refresh), - ), - ], - ), - const SizedBox(height: 8.0), - const Divider(color: AppColor.accentColor), - const SizedBox(height: 8.0), - Text( - '${'InspectionResult'.tr}: ${ai.responseCriminalRecordEgypt['InspectionResult']}'), - const SizedBox(height: 8.0), - Text( - '${'FullName'.tr}: ${ai.responseCriminalRecordEgypt['FullName']}', - style: AppStyle.title.copyWith( - color: ai.responseCriminalRecordEgypt['FullName'] == - ai.responseIdEgyptDriverLicense['name_arabic'] - ? AppColor.greenColor - : AppColor.redColor), - ), - const SizedBox(height: 8.0), - Text( - '${'NationalID'.tr}: ${ai.responseCriminalRecordEgypt['NationalID']}'), - const SizedBox(height: 8.0), - Text( - '${'IssueDate'.tr}: ${ai.responseCriminalRecordEgypt['IssueDate']}'), - ], - ), - ), - ); - } - return Card( - child: InkWell( - onTap: () async { - await ai.allMethodForAI(""" -Write a JSON object from the following information extracted from the provided Arabic text: - -{ - "InspectionResult": "", - "NationalID": "", - "FullName": "", - "IssueDate": "" // Format: YYYY-MM-DD -} - -Important notes: -1. For the IssueDate, ensure the date is in YYYY-MM-DD format using Latin numerals (0-9). -2. Add appropriate spaces in all text fields to ensure readability. -3. If any information is missing, leave the corresponding field as an empty string. -4. Ensure all text is properly formatted and spaces are used correctly. -5. Convert any Arabic numerals to Latin numerals (0-9) where applicable. - -Please fill in the JSON object with the extracted information, following these guidelines. -""", 'criminalRecord', ai.driverId); - }, - child: Column( - children: [ - Image.network( - '${AppLink.server}/card_image/6.png', - height: Get.height * .25, - width: double.maxFinite, - fit: BoxFit.fitHeight, - ), - Text( - 'Capture an Image of Your Criminal Record'.tr, - style: AppStyle.title, - ), - ], - ), - ), - ); - }, + ); + }), + ), + ], ); } } diff --git a/lib/controller/mainController/pages/welcome_call.dart b/lib/controller/mainController/pages/welcome_call.dart index 8de624d..ea0a63a 100644 --- a/lib/controller/mainController/pages/welcome_call.dart +++ b/lib/controller/mainController/pages/welcome_call.dart @@ -1,4 +1,5 @@ import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:service/constant/colors.dart'; import 'package:service/constant/style.dart'; @@ -12,21 +13,35 @@ class WelcomeCall extends StatelessWidget { @override Widget build(BuildContext context) { - Get.put(MainController()); + final controller = Get.put(MainController()); + return MyScaffold( title: 'Welcome Drivers'.tr, isleading: true, body: [ GetBuilder(builder: (mainController) { - return Expanded( - child: CupertinoScrollbar( - child: ListView.builder( - itemCount: mainController.newDriverRegister.length, - itemBuilder: (context, index) { - final driver = mainController.newDriverRegister[index]; - return DriverCard(driver: driver); - }, + 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); + }, ), ); }), @@ -39,9 +54,22 @@ class DriverCard extends StatelessWidget { final Map 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(); + return CupertinoCard( margin: const EdgeInsets.all(16.0), child: Padding( @@ -49,38 +77,37 @@ class DriverCard extends StatelessWidget { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ + // حالة التلوين حسب isCall Container( decoration: AppStyle.boxDecoration1.copyWith( - color: driver['isCall'].toString() == '1' - ? AppColor.greenColor - : AppColor.accentColor), - child: Padding( - padding: - const EdgeInsets.symmetric(horizontal: 10, vertical: 2), - child: Text( - 'Driver Information'.tr, - style: CupertinoTheme.of(context).textTheme.navTitleTextStyle, - ), + 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: 16), - InfoText('Name'.tr, driver['name_arabic'].toString()), - InfoText('Phone'.tr, driver['phone'].toString()), - InfoText('Email'.tr, driver['email'].toString()), - InfoText('License Type'.tr, driver['license_type'].toString()), + const SizedBox(height: 12), + InfoText( - 'License Categories'.tr, driver['license_categories'] ?? ''), - InfoText( - 'National Number'.tr, driver['national_number'].toString()), - InfoText('Occupation'.tr, driver['occupation'].toString()), - const SizedBox(height: 16), - Text( - 'Notes:'.tr, - style: CupertinoTheme.of(context).textTheme.navTitleTextStyle, - ), + '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: Get.find().notesController, + controller: controller.notesController, placeholder: driver['notes'] ?? 'Enter notes here...'.tr, maxLines: 3, padding: const EdgeInsets.all(12.0), @@ -89,25 +116,55 @@ class DriverCard extends StatelessWidget { borderRadius: BorderRadius.circular(8.0), ), ), + const SizedBox(height: 16), + Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - SizedBox( - width: Get.width * .4, - child: MyElevatedButton( - title: 'Call Driver'.tr, - onPressed: () { - Get.find() - .makePhoneCall(driver['phone'].toString()); - })), - CupertinoButton( - onPressed: () async { - await Get.find().addWelcomeCall( - driver['id'].toString(), - ); - }, - child: Text('Save Changes'.tr), + 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().launchCommunication( + 'whatsapp', + phone, + message, + ); + }, + child: Text('send'.tr), + ), ), ], ), @@ -120,16 +177,17 @@ class DriverCard extends StatelessWidget { class InfoText extends StatelessWidget { final String label; - final String value; + 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.only(bottom: 4.0), + padding: const EdgeInsets.symmetric(vertical: 2.0), child: Text( - '$label: $value', + '$label: $display', style: CupertinoTheme.of(context).textTheme.textStyle, ), ); diff --git a/lib/controller/mainController/ragister_service_controller.dart b/lib/controller/mainController/ragister_service_controller.dart new file mode 100644 index 0000000..f53fe0c --- /dev/null +++ b/lib/controller/mainController/ragister_service_controller.dart @@ -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:service/constant/box_name.dart'; +import 'package:service/constant/links.dart'; +import 'package:service/controller/functions/crud.dart'; +import 'package:service/main.dart'; +import 'package:service/print.dart'; +import 'package: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 = {}.obs; +// En tu archivo registration_captain_controller.dart + final phoneController = TextEditingController(); + // --- Dynamic Document Image URLs --- + final Map 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 fetchDataFromServer() async { + isLoading.value = true; + try { + var responseString = await CRUD().post( + link: AppLink.getDriverDetailsForActivate, + payload: {'driverId': driverId.value}); + + if (responseString != 'failure') { + var decodedResponse = jsonDecode(responseString); + if (decodedResponse['status'] == 'success' && + (decodedResponse['message'] as List).isNotEmpty) { + var rawData = decodedResponse['message'][0] as Map; + // 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 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( + 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 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') { + 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['message']}'.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> 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 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(); + } +} diff --git a/lib/controller/mainController/registration_captain_controller.dart b/lib/controller/mainController/registration_captain_controller.dart index 2cef643..5f568c8 100644 --- a/lib/controller/mainController/registration_captain_controller.dart +++ b/lib/controller/mainController/registration_captain_controller.dart @@ -1,1216 +1,338 @@ import 'dart:convert'; -import 'dart:math'; +import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; -import 'package:http/http.dart' as http; -import 'package:service/views/widgets/elevated_btn.dart'; +import 'package:intl/intl.dart'; +import 'package:service/constant/box_name.dart'; +import 'package:service/constant/links.dart'; +import 'package:service/controller/functions/crud.dart'; +import 'package:service/main.dart'; +import 'package:service/print.dart'; +import 'package:service/views/widgets/my_dialog.dart'; -import '../../constant/api_key.dart'; -import '../../constant/box_name.dart'; -import '../../constant/colors.dart'; -import '../../constant/info.dart'; -import '../../constant/links.dart'; -import '../../constant/style.dart'; -import '../../main.dart'; -import '../../print.dart'; -import '../../views/widgets/mycircular.dart'; -import '../functions/crud.dart'; -import '../functions/image.dart'; -import '../functions/launch.dart'; - -enum DocumentType { - carLicenseFront, - carLicenseBack, - idCardFront, - nonIdCardFront, - nonIdCardBack, - idCardBack, - driverLicense, - unknown, -} +import 'pages/registration_captain_page.dart'; class RegisterCaptainController extends GetxController { - bool isDriverSaved = false; - bool isCarSaved = false; - Map? arguments; - String? driverId; - String? email; - String? phone; - bool isEgypt = true; + // --- UI State Management --- + var isLoading = true.obs; + late PageController pageController; + var currentPageIndex = 0.obs; + var driverId = ''.obs; + var phone = ''.obs; + var serverData = {}.obs; +// En tu archivo registration_captain_controller.dart + final phoneController = TextEditingController(); + // --- Dynamic Document Image URLs --- + final Map 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(); - arguments = Get.arguments; + pageController = PageController(); - getPrompt(); + final arguments = Get.arguments; + driverId.value = arguments?['driverId']; // Fallback for testing + phone.value = arguments?['phone']; // Fallback for testing - initArguments(); + _initializeTextControllers(); + _initializeDocUrls(); + fetchDataFromServer(); } - void driveInit() { - arguments = Get.arguments; - initArguments(); + 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"; } - changeNationality() { - isEgypt = !isEgypt; - update(); + 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 fetchDataFromServer() async { + isLoading.value = true; + try { + var responseString = await CRUD().post( + link: AppLink.getDriverDetailsForActivate, + payload: {'driverId': driverId.value}); - void initArguments() { - if (arguments != null) { - driverId = arguments!['driverId']; - email = arguments!['email']; - phone = arguments!['phone_number']; - } else { - print('Arguments are null'); - } - } - - Map responseMap = {}; - Map responseCarLicenseMapJordan = {}; - Map responseBackCarLicenseMap = {}; - Map responseIdCardMap = {}; - Map responseIdCardDriverEgyptBack = {}; - Map responseForComplaint = {}; - Map responseIdCardDriverEgyptFront = {}; - Map responseIdEgyptFront = {}; - Map responseCriminalRecordEgypt = {}; - Map responseIdEgyptBack = {}; - Map responseNonIdCardFront = {}; - Map responseNonIdCardBack = {}; - Map responseIdEgyptDriverLicense = {}; - String? responseIdCardDriverEgypt1; - bool isloading = false; - var image; - DateTime now = DateTime.now(); - - bool isLoading = false; - String getExpectedDocument(String imagePath) { - switch (imagePath) { - case 'car_front': - return 'car_license_front'.tr; - case 'car_back': - return 'car_license_back'.tr; - case 'id_back': - return 'id_card_back'.tr; - case 'id_front': - return 'id_card_front'.tr; - case 'driver_license': - return 'driver_license'.tr; - default: - return 'unknown_document'.tr; - } - } - - DocumentType getExpectedType(String imagePath) { - switch (imagePath) { - case 'car_front': - return DocumentType.carLicenseFront; - case 'car_back': - return DocumentType.carLicenseBack; - case 'id_back': - return DocumentType.idCardBack; - case 'id_front': - return DocumentType.idCardFront; - case 'driver_license': - return DocumentType.driverLicense; - default: - return DocumentType.unknown; - } - } - - String getDetectedDocument(DocumentType type) { - switch (type) { - case DocumentType.carLicenseFront: - return 'car_license_front'.tr; - case DocumentType.carLicenseBack: - return 'car_license_back'.tr; - case DocumentType.idCardFront: - return 'id_card_front'.tr; - case DocumentType.idCardBack: - return 'id_card_back'.tr; - case DocumentType.driverLicense: - return 'driver_license'.tr; - default: - return 'unknown_document'.tr; - } - } - - DocumentType checkDocumentType(String text) { - // Convert text to lowercase and remove all spaces and new lines - text = text.toLowerCase().replaceAll(RegExp(r'\s+'), ''); - - // Keywords for each document type - final Map> keywords = { - DocumentType.carLicenseBack: ['شاسيه', 'موتور', 'سم٣'], - DocumentType.carLicenseFront: ['رخصةتسيير'], - DocumentType.idCardFront: [ - 'بطاقةتحقيقالشخصية', - 'بطاقة تحقيق الشخصية', - 'تحقيق' - ], - DocumentType.idCardBack: ['البطاقةساريةحتى'], - DocumentType.driverLicense: ['قيادةخاصة', 'خاصه', 'قيادة'], - }; - - // Check each document type - for (var entry in keywords.entries) { - if (entry.value.any((keyword) => text.contains(keyword))) { - return entry.key; + if (responseString != 'failure') { + var decodedResponse = jsonDecode(responseString); + if (decodedResponse['status'] == 'success' && + (decodedResponse['message'] as List).isNotEmpty) { + var rawData = decodedResponse['message'][0] as Map; + // Sanitize data: ensure all values are strings to prevent type errors + serverData.value = rawData + .map((key, value) => MapEntry(key, value?.toString() ?? '')); + _populateControllersWithServerData(); + } } - } - - // If no match is found - return DocumentType.unknown; - } - - List prompts = []; - getPrompt() async { - var res = await CRUD() - .get(link: AppLink.getPromptDriverDocumentsEgypt, payload: {}); - if (res != 'failure') { - var d = jsonDecode(res)['message']; - prompts = d; - } else {} - } - - Future allMethodForAI(String prompt, imagePath, driverID) async { - isLoading = true; - update(); - await ImageController() - .choosImage(AppLink.uploadEgypt, driverID, imagePath); - - var extractedString = await CRUD().arabicTextExtractByVisionAndAI( - imagePath: imagePath, driverID: driverID); - var json = jsonDecode(extractedString); - var textValues = extractTextFromLines(json); - Log.print('textValues: ${textValues}'); - // await Get.put(AI()).geminiAiExtraction(prompt, textValues, imagePath); - - DocumentType detectedType = checkDocumentType(textValues); - String expectedDocument = getExpectedDocument(imagePath); - String detectedDocument = getDetectedDocument(detectedType); - - // bool isCorrectDocument = (detectedType == getExpectedType(imagePath)); - // if (!isCorrectDocument) { - // Get.defaultDialog( - // title: 'incorrect_document_title'.tr, - // middleText: - // '${'expected'.tr}: $expectedDocument\n${'detected'.tr}: $detectedDocument', - // confirm: MyElevatedButton( - // title: 'OK'.tr, - // onPressed: () { - // Get.back(); - // })); - // } else { - // Process the correct document - await anthropicAI(textValues, prompt, imagePath); - // } - isLoading = false; - update(); - } - - String extractTextFromLines(Map jsonData) { - final readResult = jsonData['readResult']; - final blocks = readResult['blocks']; - - final StringBuffer buffer = StringBuffer(); - - for (final block in blocks) { - final lines = block['lines']; - for (final line in lines) { - final text = line['text']; - buffer.write(text); - buffer.write('\n'); - } - } - - return buffer.toString().trim(); - } - - List driverNotCompleteRegistration = []; - getDriverNotCompleteRegistration() async { - var res = await CRUD() - .get(link: AppLink.getDriverNotCompleteRegistration, payload: {}); - if (res != 'failure') { - var d = jsonDecode(res)['message']; - driverNotCompleteRegistration = d; - update(); - } else { - Get.snackbar(res, ''); + } catch (e) { + Get.snackbar('Error', 'An unexpected error occurred: $e'); + } finally { + isLoading.value = false; } } - final today = DateTime.now(); + 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'] ?? ''; - Future addDriverAndCarEgypt() async { - final expiryDate = responseIdEgyptDriverLicense['expiry_date']; - final expiryDateTime = DateTime.tryParse(expiryDate); - Log.print('expiryDateTime: ${expiryDateTime}'); - final isExpired = expiryDateTime != null && expiryDateTime.isBefore(today); + 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']!; - final taxExpiryDate = responseIdCardDriverEgyptBack['tax_expiry']; - Log.print('taxExpiryDate: ${taxExpiryDate}'); + 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'] ?? ''; + } - // Get the inspection date from the response - final inspectionDate = responseIdCardDriverEgyptBack['inspection_date']; - final year = int.parse(inspectionDate.split('-')[0]); - // Try parsing the tax expiry date. If it fails, set it to null. - final taxExpiryDateTime = DateTime.tryParse(taxExpiryDate ?? ''); - Log.print('taxExpiryDateTime: ${taxExpiryDateTime}'); - final isExpiredCar = - taxExpiryDateTime != null && taxExpiryDateTime.isBefore(today); + Future 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 */} - // Check if the inspection date is before today - final inspectionDateTime = DateTime(year, 12, 31); - final isInspectionExpired = inspectionDateTime.isBefore(today); - - if (isExpiredCar || isInspectionExpired) { - Get.defaultDialog( - title: 'Expired Driver’s License'.tr, - content: Column( - mainAxisSize: MainAxisSize.min, + DateTime? pickedDate = initialDate; + await showCupertinoModalPopup( + context: context, + builder: (BuildContext context) => Container( + height: 280, + color: CupertinoColors.systemBackground.resolveFrom(context), + child: Column( children: [ - const Icon(Icons.warning, size: 48, color: Colors.red), - const SizedBox(height: 16), - Text( - "Your driver’s license and/or car tax has expired. Please renew them before proceeding." - .tr, - textAlign: TextAlign.center, - style: AppStyle.title, + SizedBox( + height: 200, + child: CupertinoDatePicker( + initialDateTime: initialDate, + minimumDate: DateTime(1950), + maximumDate: DateTime(2050), + mode: CupertinoDatePickerMode.date, + onDateTimeChanged: (DateTime newDate) { + pickedDate = newDate; + }, + ), ), - const SizedBox(height: 16), - IconButton( - onPressed: () async { - // await Get.find().speakText( - // 'Your driver’s license and/or car tax has expired. Please renew them before proceeding.' - // .tr, - // ); + CupertinoButton( + child: Text('Done'.tr), + onPressed: () { + if (pickedDate != null) + controller.text = + DateFormat('yyyy-MM-dd').format(pickedDate!); + Navigator.of(context).pop(); }, - icon: const Icon(Icons.volume_up), ), ], ), - actions: [ - TextButton( - onPressed: () { - Get.back(); - }, - child: const Text('OK'), - ), - ], - ); - } else if (isExpired) { - Get.defaultDialog( - title: 'Expired Driver’s License'.tr, - content: Column( - mainAxisSize: MainAxisSize.min, - children: [ - const Icon(Icons.warning, size: 48, color: Colors.red), - const SizedBox(height: 16), - Text( - "Your driver’s license has expired. Please renew it before proceeding." - .tr, - textAlign: TextAlign.center, - style: AppStyle.title, - ), - const SizedBox(height: 16), - IconButton( - onPressed: () async { - // await Get.find().speakText( - // 'Your driver’s license has expired. Please renew it before proceeding.' - // .tr, - // ); - }, - icon: const Icon(Icons.volume_up), - ), - ], - ), - actions: [ - TextButton( - onPressed: () { - Get.back(); - }, - child: const Text('OK'), - ), - ], - ); - } - // else if (responseIdEgyptDriverLicense['national_number'] - // .toString() - // .substring(0, 12) != - // responseIdEgyptBack['nationalID'].toString().substring(0, 12)) { - // Get.defaultDialog( - // barrierDismissible: false, - // title: 'ID Mismatch', - // content: Column( - // mainAxisSize: MainAxisSize.min, - // children: [ - // const Icon(Icons.warning, size: 48, color: Colors.red), - // const SizedBox(height: 16), - // Text( - // "The national number on your driver’s license does not match the one on your ID document. Please verify and provide the correct documents." - // .tr, - // textAlign: TextAlign.center, - // style: AppStyle.title, - // ), - // const SizedBox(height: 16), - // IconButton( - // onPressed: () async { - // // await Get.find().speakText( - // // 'The national number on your driver’s license does not match the one on your ID document. Please verify and provide the correct documents.', - // // ); - // }, - // icon: const Icon(Icons.volume_up), - // ), - // ], - // ), - // actions: [ - // TextButton( - // onPressed: () { - // Get.back(); - // }, - // child: const Text('OK'), - // ), - // ], - // ); - // } - // else if (responseCriminalRecordEgypt['FullName'] != - // responseIdEgyptDriverLicense['name_arabic']) { - // Get.defaultDialog( - // barrierDismissible: false, - // title: 'Criminal Record Mismatch', - // content: Column( - // mainAxisSize: MainAxisSize.min, - // children: [ - // const Icon(Icons.warning, size: 48, color: Colors.red), - // const SizedBox(height: 16), - // Text( - // 'The full name on your criminal record does not match the one on your driver’s license. Please verify and provide the correct documents.' - // .tr, - // textAlign: TextAlign.center, - // style: AppStyle.title, - // ), - // const SizedBox(height: 16), - // IconButton( - // onPressed: () async { - // await Get.find().speakText( - // 'The full name on your criminal record does not match the one on your driver’s license. Please verify and provide the correct documents.' - // .tr, - // ); - // }, - // icon: const Icon(Icons.volume_up), - // ), - // ], - // ), - // actions: [ - // TextButton( - // onPressed: () { - // Get.back(); - // }, - // child: const Text('OK'), - // ), - // ], - // ); - // } - else { - await addDriverEgypt(); - await addRegistrationCarEgypt(); - - if (isCarSaved && isDriverSaved) { - Get.snackbar('added', '', - backgroundColor: - AppColor.greenColor); // Get.offAll(() => HomeCaptain()); - // Get.offAll(() => HomeCaptain()); - } - } + ), + ); } - String extractDOB(String nationalNumber) { - if (nationalNumber.length != 14) { - Get.snackbar('National number must be 14 digits long.', 'message', - backgroundColor: AppColor.redColor); - throw ArgumentError('National number must be 14 digits long.'); - } - - // Extract the first digit to determine the century - String firstDigit = nationalNumber[0]; - - // Extract year, month, and day parts - String yearPart = nationalNumber.substring(1, 3); - String monthPart = nationalNumber.substring(3, 5); - String dayPart = nationalNumber.substring(5, 7); - - // Determine the year based on the first digit - int yearPrefix; - if (firstDigit == '2') { - yearPrefix = 1900; - } else if (firstDigit == '3') { - yearPrefix = 2000; - } else { - throw ArgumentError('Invalid first digit in national number.'); - } - - // Construct the full year - int year = yearPrefix + int.parse(yearPart); - - // Format the date as YYYY-MM-DD - String dob = - '$year-${monthPart.padLeft(2, '0')}-${dayPart.padLeft(2, '0')}'; - - return dob; + void nextPage() { + if (currentPageIndex.value < 3) + pageController.nextPage( + duration: const Duration(milliseconds: 400), curve: Curves.easeInOut); } - String generatePassword({int length = 8}) { - const String upperCaseLetters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'; - const String lowerCaseLetters = 'abcdefghijklmnopqrstuvwxyz'; - const String digits = '0123456789'; - const String specialCharacters = '!@#\$%^&*()-_=+[]{}|;:,.<>?'; - - const String allChars = - upperCaseLetters + lowerCaseLetters + digits + specialCharacters; - final Random random = Random(); - - // Generate a random password of the specified length - String password = List.generate(length, (index) { - int randomIndex = random.nextInt(allChars.length); - return allChars[randomIndex]; - }).join(); - - return password; + void previousPage() { + if (currentPageIndex.value > 0) + pageController.previousPage( + duration: const Duration(milliseconds: 400), curve: Curves.easeInOut); } - Future addDriverForeign() async { - isLoading = true; - update(); - var added = await storage.read(key: 'name'); - String newPassword = generatePassword(); - print('Generated Password: $newPassword'); - var payload = { - 'first_name': - responseNonIdCardFront['full_name']?.toString().split(' ')[0] ?? - 'Not specified', - 'last_name': - responseNonIdCardFront['full_name']?.toString().split(' ').last ?? - 'Not specified', - 'email': box.read(BoxName.emailDriver)?.toString() ?? 'Not specified', - 'phone': box.read(BoxName.phoneDriver)?.toString() ?? 'Not specified', - 'id': box.read(BoxName.driverID)?.toString() ?? 'Not specified', - 'password': newPassword.toString() ?? '123456', - 'gender': responseNonIdCardFront['gender']?.toString() ?? 'Not specified', - 'license_type': 'Foreign', - 'national_number': - responseNonIdCardFront['passport_no']?.toString() ?? 'Not specified', - 'name_arabic': - responseNonIdCardFront['full_name']?.toString() ?? 'Not specified', - 'name_english': 'Not specified', - 'issue_date': - responseNonIdCardBack['issueDate']?.toString() ?? 'Not specified', - 'expiry_date': - responseNonIdCardBack['residencyExpirationDate']?.toString() ?? - 'Not specified', - 'license_categories': responseIdEgyptDriverLicense['license_categories'] - is List - ? responseIdEgyptDriverLicense['license_categories'].join(', ') - : responseIdEgyptDriverLicense['license_categories']?.toString() ?? - 'Not specified', - 'address': - responseNonIdCardFront['address']?.toString() ?? 'Not specified', - 'card_id': - responseNonIdCardFront['card_id']?.toString() ?? 'Not specified', - 'occupation': - responseNonIdCardBack['workStatus']?.toString() ?? 'Not specified', - 'education': 'Not specified', - 'licenseIssueDate': - responseNonIdCardBack['issueDate']?.toString() ?? 'Not specified', - 'religion': - responseNonIdCardFront['country']?.toString() ?? 'Not specified', - 'status': 'yet', - 'birthdate': - responseNonIdCardFront['birthdate']?.toString() ?? 'Not specified', - 'maritalStatus': added.toString(), - 'site': responseNonIdCardFront['address']?.toString() ?? 'Not specified', - 'employmentType': - responseNonIdCardBack['residencyType']?.toString() ?? 'Not specified', + void updateAndActivateSyrianDriver() { + isLoading.value = true; + + Map 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, }; - try { - var res = await CRUD().post(link: AppLink.signUpCaptin, payload: payload); + print("--- Submitting Data to Server ---"); + print(dataToSend); - var status1; - try { - status1 = jsonDecode(res); - } catch (e) { - throw FormatException("Invalid JSON response: $res"); - } - - isLoading = false; - update(); - - if (status1['status'] == 'success') { - isDriverSaved = true; - CRUD().post( - link: '${AppLink.seferGizaServer}/auth/captin/register.php', - payload: payload); - CRUD().post( - link: '${AppLink.seferAlexandriaServer}/auth/captin/register.php', - payload: payload); - mySnackbarSuccess('Foreign driver data saved successfully'); + CRUD() + .post(link: AppLink.updateDriverToActive, payload: dataToSend) + .then((response) { + isLoading.value = false; + var decodedResponse = (response); + Log.print('decodedResponse: ${decodedResponse}'); + if (decodedResponse != 'failure') { + MyDialog().getDialog('Success'.tr, '', + Text('Driver has been activated successfully!'.tr), () { + Get.back(); + Get.back(); + // fetchDataFromServer(); + Get.back(); + // Get.off(() => RegisterCaptain()); + }); } else { - mySnackeBarError( - '${'Failed to save driver data'.tr}: ${status1['message']}'); + Get.snackbar( + 'Error'.tr, + 'Failed to update driver: ${decodedResponse['message']}'.tr, + backgroundColor: Colors.red, + colorText: Colors.white, + ); } - } catch (e) { - isLoading = false; - update(); - mySnackeBarError( - 'An error occurred while saving driver data'.tr, + }).catchError((error) { + isLoading.value = false; + Get.snackbar( + 'Error'.tr, + 'An error occurred: $error'.tr, + backgroundColor: Colors.red, + colorText: Colors.white, ); - } - } - - Future addDriverEgypt() async { - isLoading = true; - update(); - var added = await storage.read(key: 'name'); - String newPassword = generatePassword(); - print('Generated Password: $newPassword'); - var payload = { - 'first_name': responseIdEgyptDriverLicense['firstName']?.toString() ?? - 'Not specified', - 'last_name': responseIdEgyptDriverLicense['lastName']?.toString() ?? - 'Not specified', - 'email': email?.toString() ?? 'Not specified', - 'phone': phone?.toString() ?? 'Not specified', - 'id': driverId?.toString() ?? 'Not specified', - 'password': newPassword.toString() ?? '123456', - 'gender': responseIdEgyptBack['gender']?.toString() ?? 'Not specified', - 'license_type': - responseIdEgyptDriverLicense['license_type']?.toString() ?? - 'Not specified', - 'national_number': - responseIdEgyptBack['nationalID']?.toString() ?? 'Not specified', - 'name_arabic': responseIdEgyptDriverLicense['name_arabic']?.toString() ?? - 'Not specified', - 'name_english': - responseIdEgyptDriverLicense['name_english']?.toString() ?? - 'Not specified', - 'issue_date': responseIdEgyptDriverLicense['issue_date']?.toString() ?? - 'Not specified', - 'expiry_date': responseIdEgyptDriverLicense['expiry_date']?.toString() ?? - 'Not specified', - 'license_categories': responseIdEgyptDriverLicense['license_categories'] - is List - ? responseIdEgyptDriverLicense['license_categories'].join(', ') - : responseIdEgyptDriverLicense['license_categories']?.toString() ?? - 'Not specified', - 'address': responseIdEgyptFront['address']?.toString() ?? 'Not specified', - 'card_id': responseIdEgyptFront['card_id']?.toString() ?? 'Not specified', - 'occupation': - responseIdEgyptBack['occupation']?.toString() ?? 'Not specified', - 'education': - responseIdEgyptBack['occupation']?.toString() ?? 'Not specified', - 'licenseIssueDate': - responseIdEgyptDriverLicense['issue_date']?.toString() ?? - 'Not specified', - 'religion': - responseIdEgyptBack['religion']?.toString() ?? 'Not specified', - 'status': 'yet', - 'birthdate': extractDOB( - responseIdEgyptDriverLicense['national_number'].toString()), - 'maritalStatus': added.toString(), - 'site': responseIdEgyptDriverLicense['address']?.toString() ?? - 'Not specified', - 'employmentType': - responseIdEgyptDriverLicense['employmentType']?.toString() ?? - 'Not specified', - }; - var res = await CRUD().post(link: AppLink.signUpCaptin, payload: payload); - var status1 = jsonDecode(res); - isLoading = false; - update(); - // Handle response - if (status1['status'] == 'success') { - isDriverSaved = true; - var res = CRUD().post( - link: '${AppLink.seferGizaServer}/auth/captin/register.php', - payload: payload); - Log.print('res: ${res}'); - var res1 = CRUD().post( - link: '${AppLink.seferAlexandriaServer}/auth/captin/register.php', - payload: payload); - Log.print('res: ${res1}'); - Get.snackbar('Success', 'Driver data saved successfully', - backgroundColor: AppColor.greenColor); - launchCommunication('whatsapp', phone.toString(), - 'you have seuccessfully registered in SEFER\n\n\n*Email* is *$email*\n\n*id* is *$driverId*\n\n*password* is *$newPassword*'); - } else { - Get.snackbar('Error', 'Failed to save driver data', - backgroundColor: Colors.red); - } - } - - Future addDriverEgyptHanding() async { - isLoading = true; - update(); - String newPassword = generatePassword(); - var added = await storage.read(key: 'name'); - var payload = { - 'first_name': firstName.value.isNotEmpty - ? firstName.value - : responseIdEgyptDriverLicense['firstName'], - 'last_name': lastName.value.isNotEmpty - ? lastName.value - : responseIdEgyptDriverLicense['lastName'], - 'email': email?.toString() ?? 'Not specified', - 'phone': phone?.toString() ?? 'Not specified', - 'id': driverId?.toString() ?? 'Not specified', - 'password': newPassword.toString() ?? '123456', - 'gender': gender.value.isNotEmpty - ? gender.value - : responseIdEgyptBack['gender'], - 'license_type': licenseType.value.isNotEmpty - ? licenseType.value - : responseIdEgyptDriverLicense['license_type'], - 'national_number': nationalNumber.value.isNotEmpty - ? nationalNumber.value - : responseIdEgyptDriverLicense['national_number'], - 'name_arabic': nameArabic.value.isNotEmpty - ? nameArabic.value - : responseIdEgyptDriverLicense['name_arabic'], - 'name_english': nameEnglish.value.isNotEmpty - ? nameEnglish.value - : responseIdEgyptDriverLicense['name_english'], - 'issue_date': issueDate.value.isNotEmpty - ? issueDate.value - : responseIdEgyptDriverLicense['issue_date'], - 'expiry_date': expiryDate.value.isNotEmpty - ? expiryDate.value - : responseIdEgyptDriverLicense['expiry_date'], - 'license_categories': licenseCategories.value.isNotEmpty - ? licenseCategories.value - : responseIdEgyptDriverLicense['license_categories'] is List - ? responseIdEgyptDriverLicense['license_categories'].join(', ') - : responseIdEgyptDriverLicense['license_categories'], - 'address': address.value.isNotEmpty - ? address.value - : responseIdEgyptFront['address'], - 'card_id': cardId.value.isNotEmpty - ? cardId.value - : responseIdEgyptFront['card_id'], - 'occupation': occupation.value.isNotEmpty - ? occupation.value - : responseIdEgyptBack['occupation'], - 'education': education.value.isNotEmpty - ? education.value - : responseIdEgyptDriverLicense['issue_date'], - 'licenseIssueDate': licenseIssueDate.value.isNotEmpty - ? licenseIssueDate.value - : responseIdEgyptBack['religion'], - 'religion': religion.value.isNotEmpty ? religion.value : 'Not specified', - 'status': status.value.isNotEmpty ? status.value : 'yet', - 'birthdate': - responseIdEgyptDriverLicense['national_number'].toString().length < 14 - ? extractDOB(responseIdEgyptBack['nationalID']) - : extractDOB( - responseIdEgyptDriverLicense['national_number'].toString()), - 'maritalStatus': added.toString(), - 'fullNameMaritial': firstName.value.isNotEmpty - ? firstName.value - : responseIdEgyptDriverLicense['firstName'] + - '_' + - newPassword.toString(), - 'site': site.value.isNotEmpty - ? site.value - : responseIdEgyptDriverLicense['address'], - 'employmentType': employmentType.value.isNotEmpty - ? employmentType.value - : responseIdEgyptDriverLicense['employmentType'], - }; - - var res = await CRUD().post(link: AppLink.signUpCaptin, payload: payload); - var status1 = jsonDecode(res); - isLoading = false; - update(); - - // Handle response - if (status1['status'] == 'success') { - isDriverSaved = true; - CRUD().post( - link: '${AppLink.seferGizaServer}/auth/captin/register.php', - payload: payload); - CRUD().post( - link: '${AppLink.seferAlexandriaServer}/auth/captin/register.php', - payload: payload); - Get.snackbar('Success', 'Driver data saved successfully', - backgroundColor: AppColor.greenColor); - launchCommunication('whatsapp', phone.toString(), - 'you have seuccessfully registered in SEFER\n\n\n*Email* is *$email*\n\n*id* is *$driverId*\n\n*password* is *$newPassword*'); - } else { - Get.snackbar('Error', 'Failed to save driver data', - backgroundColor: Colors.red); - } - } - - addCriminalDeocuments() async { - var res = await CRUD().post(link: AppLink.addCriminalDocuments, payload: { - "driverId": box.read(BoxName.driverID), - "IssueDate": responseCriminalRecordEgypt['IssueDate'], - "InspectionResult": responseCriminalRecordEgypt['InspectionResult'], }); - if (res != 'failure') { - Get.snackbar('uploaded sucssefuly'.tr, ''); - CRUD().post( - link: - '${AppLink.seferAlexandriaServer}/auth/captin/addCriminalDocuments.php', - payload: { - "driverId": box.read(BoxName.driverID), - "IssueDate": responseCriminalRecordEgypt['IssueDate'], - "InspectionResult": responseCriminalRecordEgypt['InspectionResult'], - }); - CRUD().post( - link: - '${AppLink.seferGizaServer}/auth/captin/addCriminalDocuments.php', - payload: { - "driverId": box.read(BoxName.driverID), - "IssueDate": responseCriminalRecordEgypt['IssueDate'], - "InspectionResult": responseCriminalRecordEgypt['InspectionResult'], - }); - } } - var firstName = ''.obs; - var lastName = ''.obs; - var id = ''.obs; - var password = '123456'.obs; - var gender = ''.obs; - var licenseType = ''.obs; - var nationalNumber = ''.obs; - var nameArabic = ''.obs; - var nameEnglish = ''.obs; - var issueDate = ''.obs; - var expiryDate = ''.obs; - var licenseCategories = ''.obs; - var address = ''.obs; - var cardId = ''.obs; - var occupation = ''.obs; - var education = ''.obs; - var licenseIssueDate = ''.obs; - var religion = ''.obs; - var status = 'yet'.obs; - var birthdate = ''.obs; - var maritalStatus = ''.obs; - var site = ''.obs; - var employmentType = ''.obs; - var vin = ''.obs; - var carPlate = ''.obs; - var make = ''.obs; - var model = ''.obs; - var year = ''.obs; - var expirationDate = ''.obs; - var color = ''.obs; - var owner = ''.obs; - var colorHex = ''.obs; - var addressCar = ''.obs; - var displacement = ''.obs; - var fuel = ''.obs; - var registrationDate = ''.obs; + static const List> 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'}, + ]; - Future addRegistrationCarEgypt() async { - try { - final inspectionDate = - responseIdCardDriverEgyptBack['inspection_date'].toString(); - final year = int.parse(inspectionDate.split('-')[0]); - final inspectionDateTime = DateTime(year, 12, 31); - isLoading = true; - update(); - var res = await CRUD().post(link: AppLink.addRegisrationCar, payload: { - 'driverID': driverId, - 'vin': responseIdCardDriverEgyptBack['chassis'].toString(), - 'car_plate': responseIdCardDriverEgyptFront['car_plate'].toString(), - 'make': responseIdCardDriverEgyptBack['make'].toString(), - 'model': responseIdCardDriverEgyptBack['model'], - 'year': responseIdCardDriverEgyptBack['year'].toString(), - 'expiration_date': - responseIdCardDriverEgyptFront['LicenseExpirationDate'].toString(), - 'color': responseIdCardDriverEgyptBack['color'], - 'owner': responseIdCardDriverEgyptFront['owner'], - 'color_hex': responseIdCardDriverEgyptBack['color_hex'].toString(), - 'address': responseIdCardDriverEgyptFront['address'].toString(), - 'displacement': responseIdCardDriverEgyptBack['engine'].toString(), - 'fuel': responseIdCardDriverEgyptBack['fuel'].toString(), - 'registration_date': '$inspectionDateTime', - }); - isLoading = false; - update(); - var status = jsonDecode(res); - if (status['status'] == 'success') { - isCarSaved = true; - CRUD().post( - link: - '${AppLink.seferAlexandriaServer}/ride/RegisrationCar/add.php', - payload: { - 'driverID': driverId, - 'vin': responseIdCardDriverEgyptBack['chassis'].toString(), - 'car_plate': - responseIdCardDriverEgyptFront['car_plate'].toString(), - 'make': responseIdCardDriverEgyptBack['make'].toString(), - 'model': responseIdCardDriverEgyptBack['model'], - 'year': responseIdCardDriverEgyptBack['year'].toString(), - 'expiration_date': - responseIdCardDriverEgyptFront['LicenseExpirationDate'] - .toString(), - 'color': responseIdCardDriverEgyptBack['color'], - 'owner': responseIdCardDriverEgyptFront['owner'], - 'color_hex': - responseIdCardDriverEgyptBack['color_hex'].toString(), - 'address': responseIdCardDriverEgyptFront['address'].toString(), - 'displacement': - responseIdCardDriverEgyptBack['engine'].toString(), - 'fuel': responseIdCardDriverEgyptBack['fuel'].toString(), - 'registration_date': '$inspectionDateTime', - }); - CRUD().post( - link: '${AppLink.seferGizaServer}/ride/RegisrationCar/add.php', - payload: { - 'driverID': driverId, - 'vin': responseIdCardDriverEgyptBack['chassis'].toString(), - 'car_plate': - responseIdCardDriverEgyptFront['car_plate'].toString(), - 'make': responseIdCardDriverEgyptBack['make'].toString(), - 'model': responseIdCardDriverEgyptBack['model'], - 'year': responseIdCardDriverEgyptBack['year'].toString(), - 'expiration_date': - responseIdCardDriverEgyptFront['LicenseExpirationDate'] - .toString(), - 'color': responseIdCardDriverEgyptBack['color'], - 'owner': responseIdCardDriverEgyptFront['owner'], - 'color_hex': - responseIdCardDriverEgyptBack['color_hex'].toString(), - 'address': responseIdCardDriverEgyptFront['address'].toString(), - 'displacement': - responseIdCardDriverEgyptBack['engine'].toString(), - 'fuel': responseIdCardDriverEgyptBack['fuel'].toString(), - 'registration_date': '$inspectionDateTime', - }); + static const List kFuelOptions = [ + 'بنزين', + 'ديزل', + 'هايبرد', + 'كهربائي' + ]; - Get.snackbar('Success', 'message', - backgroundColor: AppColor.greenColor); - } - } catch (e) {} + 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']!; } - Future addRegistrationCarEgyptHandling() async { - try { - final inspectionDate = - responseIdCardDriverEgyptBack['inspection_date']?.toString() ?? ''; - final year = inspectionDate.isNotEmpty - ? int.parse(inspectionDate.split('-')[0]) - : DateTime.now().year; - final inspectionDateTime = DateTime(year, 12, 31); - Log.print('inspectionDateTime: $inspectionDateTime'); - - isLoading = true; - update(); - - var payload = { - 'driverID': driverId, - 'vin': vin.value.isNotEmpty - ? vin.value - : responseIdCardDriverEgyptBack['chassis']?.toString() ?? '', - 'car_plate': carPlate.value.isNotEmpty - ? carPlate.value - : responseIdCardDriverEgyptFront['car_plate']?.toString() ?? '', - 'make': make.value.isNotEmpty - ? make.value - : responseIdCardDriverEgyptBack['make']?.toString() ?? '', - 'model': model.value.isNotEmpty - ? model.value - : responseIdCardDriverEgyptBack['model']?.toString() ?? '', - 'year': year.toString(), - 'expiration_date': expirationDate.value.isNotEmpty - ? expirationDate.value - : responseIdCardDriverEgyptFront['LicenseExpirationDate'] - ?.toString() ?? - '', - 'color': color.value.isNotEmpty - ? color.value - : responseIdCardDriverEgyptFront['color']?.toString() ?? '', - 'owner': owner.value.isNotEmpty - ? owner.value - : responseIdCardDriverEgyptFront['owner']?.toString() ?? '', - 'color_hex': getColorHex(color.value), - 'address': addressCar.value.isNotEmpty - ? addressCar.value - : responseIdCardDriverEgyptFront['address']?.toString() ?? '', - 'displacement': displacement.value.isNotEmpty - ? displacement.value - : responseIdCardDriverEgyptBack['engine']?.toString() ?? '', - 'fuel': fuel.value.isNotEmpty - ? fuel.value - : responseIdCardDriverEgyptBack['fuel']?.toString() ?? '', - 'registration_date': inspectionDateTime.toIso8601String(), - }; - - Log.print('Payload: $payload'); - - var res = - await CRUD().post(link: AppLink.addRegisrationCar, payload: payload); - - isLoading = false; - update(); - - var status = jsonDecode(res); - Log.print('res: $res'); - Log.print('status: $status'); - - if (status['status'] == 'success') { - isCarSaved = true; - 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); - } - } - - String getColorHex(String colorName) { - Map colorMap = { - 'white'.tr: '#FFFFFF', - 'maroon'.tr: '#800000', // Nabeeti - 'red'.tr: '#FF0000', - 'gray'.tr: '#808080', - 'green'.tr: '#008000', - 'navy blue'.tr: '#000080', - 'pink'.tr: '#FFC0CB', - 'black'.tr: '#000000', - 'dark blue'.tr: '#003366', - 'turquoise'.tr: '#40E0D0', - 'blue'.tr: '#0000FF', - 'red ochre'.tr: '#C72C48', - 'silver'.tr: '#C0C0C0', - 'mocha'.tr: '#3B2E2A', - 'tawny'.tr: '#D2B48C', - 'gold'.tr: '#FFD700', - 'verdi'.tr: '#008000', - 'orange'.tr: '#FFA500', - 'peach'.tr: '#FFDAB9', - 'brown'.tr: '#A52A2A', - 'raw gray'.tr: '#6C6E6E', - 'champagne'.tr: '#F7E7CE', // Champagne - 'bronze'.tr: '#CD7F32', // Bronze - 'red'.tr: '#FF0000', // Red - 'maroon'.tr: '#800000' // Maroon - }; - - return colorMap[colorName.toLowerCase()] ?? - '#000000'; // Default to black if color name is not found - } - - Future getComplaintDataToAI() async { - var res = await CRUD().get( - link: AppLink.getComplaintAllDataForDriver, - payload: {'driver_id': driverId.toString()}, - ); - if (res != 'failure') { - var d = jsonDecode(res)['message']; - return d; - } else { - return [ - {'data': 'no data'} - ]; - } - } - - Future anthropicAIForComplaint() async { - var dataComplaint = await getComplaintDataToAI(); - var messagesData = [ - { - "role": "user", - "content": [ - { - "type": "text", - "text": "$dataComplaint ${AppInformation.complaintPrompt} " - } - ] - } - ]; - var requestBody = jsonEncode({ - "model": "claude-3-haiku-20240307", - "max_tokens": 1024, - "temperature": 0, - "system": "Json output only without any additional ", - "messages": messagesData, - }); - final response = await http.post( - Uri.parse('https://api.anthropic.com/v1/messages'), - headers: { - 'x-api-key': AK.anthropicAIkeySeferNew, - 'anthropic-version': '2023-06-01', - 'content-type': 'application/json' - }, - body: requestBody, - ); - if (response.statusCode == 200) { - var responseData = jsonDecode(utf8.decode(response.bodyBytes)); - // Process the responseData as needed - - responseForComplaint = jsonDecode(responseData['content'][0]['text']); - } - } - - Future anthropicAI( - String payload, String prompt, String idType) async { - var messagesData = [ - { - "role": "user", - "content": [ - {"type": "text", "text": "$payload $prompt"} - ] - } - ]; - - var requestBody = jsonEncode({ - "model": "claude-3-haiku-20240307", - "max_tokens": 1024, - "temperature": 0, - "system": "Json output only without any additional ", - "messages": messagesData, - }); - - final response = await http.post( - Uri.parse('https://api.anthropic.com/v1/messages'), - headers: { - 'x-api-key': AK.anthropicAIkeySeferNewHamzaayedpython, - 'anthropic-version': '2023-06-01', - 'content-type': 'application/json' - }, - body: requestBody, - ); - Log.print('responseData: ${response.body}'); - if (response.statusCode == 200) { - var responseData = jsonDecode(utf8.decode(response.bodyBytes)); - Log.print('responseData: ${responseData}'); - // Process the responseData as needed - if (idType == 'car_back') { - responseIdCardDriverEgyptBack = - jsonDecode(responseData['content'][0]['text']); - } else if (idType == 'car_front') { - responseIdCardDriverEgyptFront = - jsonDecode(responseData['content'][0]['text']); - } else if (idType == 'id_front') { - responseIdEgyptFront = jsonDecode(responseData['content'][0]['text']); - } else if (idType == 'id_back') { - responseIdEgyptBack = jsonDecode(responseData['content'][0]['text']); - } else if (idType == 'driver_license') { - responseIdEgyptDriverLicense = - jsonDecode(responseData['content'][0]['text']); - } else if (idType == 'criminalRecord') { - responseCriminalRecordEgypt = - jsonDecode(responseData['content'][0]['text']); - } - - update(); - return responseData.toString(); - } - return responseIdCardDriverEgyptBack.toString(); - } - - Future geminiAiExtraction(String prompt, payload, String idType) async { - var requestBody = jsonEncode({ - "contents": [ - { - "parts": [ - {"text": "$payload $prompt"} - ] - } - ], - "generationConfig": { - "temperature": 1, - "topK": 64, - "topP": 0.95, - "maxOutputTokens": 8192, - "stopSequences": [] - }, - "safetySettings": [ - { - "category": "HARM_CATEGORY_HARASSMENT", - "threshold": "BLOCK_MEDIUM_AND_ABOVE" - }, - { - "category": "HARM_CATEGORY_HATE_SPEECH", - "threshold": "BLOCK_MEDIUM_AND_ABOVE" - }, - { - "category": "HARM_CATEGORY_SEXUALLY_EXPLICIT", - "threshold": "BLOCK_MEDIUM_AND_ABOVE" - }, - { - "category": "HARM_CATEGORY_DANGEROUS_CONTENT", - "threshold": "BLOCK_MEDIUM_AND_ABOVE" - } - ] - }); - - final response = await http.post( - Uri.parse(''), - // 'https://generativelanguage.googleapis.com/v1beta/models/gemini-pro-vision:generateContent?key=${AK.geminiApi}'), - // 'https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-pro-latest:generateContent?key=${AK.geminiApi}'), - // 'https://generativelanguage.googleapis.com/v1beta/models/gemini-1.0-pro:generateContent?key=${AK.geminiApi}'), - headers: {'Content-Type': 'application/json'}, - body: requestBody, - ); - - if (response.statusCode == 200) { - var responseData = jsonDecode(response.body); - // Process the responseData as needed - - var result = responseData['candidates'][0]['content']['parts'][0]['text']; - RegExp regex = RegExp(r"```json([^`]*)```"); - String? jsonString = - regex.firstMatch(responseData.toString())?.group(1)?.trim(); - - if (jsonString != null) { - // Convert the JSON object to a String - jsonString = jsonEncode(json.decode(jsonString)); - - if (idType == 'car_back') { - responseIdCardDriverEgyptBack = jsonDecode(jsonString); - } else if (idType == 'car_front') { - responseIdCardDriverEgyptFront = jsonDecode(jsonString); - } else if (idType == 'id_front') { - responseIdEgyptFront = jsonDecode(jsonString); - } else if (idType == 'id_back') { - responseIdEgyptBack = jsonDecode(jsonString); - } else if (idType == 'driver_license') { - responseIdEgyptDriverLicense = jsonDecode(jsonString); - } - - update(); - } else { - Get.snackbar('Error', "JSON string not found", - backgroundColor: AppColor.redColor); - } - - // Rest of your code... - } else {} + @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(); } } diff --git a/lib/login_page.dart b/lib/login_page.dart index 8a88589..c63b9db 100644 --- a/lib/login_page.dart +++ b/lib/login_page.dart @@ -1,6 +1,7 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; +import 'package:service/main.dart'; import 'package:service/views/widgets/my_textField.dart'; import 'controller/login_controller.dart'; diff --git a/lib/main.dart b/lib/main.dart index 1f3c28d..ae31a4e 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,7 +1,10 @@ +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:service/controller/firbase_messge.dart'; +import 'package:service/firebase_options.dart'; import 'controller/functions/encrypt_decrypt.dart'; import 'controller/functions/initilize.dart'; @@ -14,31 +17,15 @@ const storage = FlutterSecureStorage(); void main() async { WidgetsFlutterBinding.ensureInitialized(); - // if (Platform.isAndroid || Platform.isIOS) { - // await Firebase.initializeApp( - // options: DefaultFirebaseOptions.currentPlatform, - // ); - // await FirebaseMessagesController().requestFirebaseMessagingPermission(); - // // FirebaseMessaging.onBackgroundMessage(backgroundMessageHandler); - - // List initializationTasks = [ - // FirebaseMessagesController().getNotificationSettings(), - // FirebaseMessagesController().getToken(), - // ]; - // // cameras = await availableCameras(); - // await Future.wait(initializationTasks); - // SystemChrome.setPreferredOrientations([ - // DeviceOrientation.portraitUp, - // DeviceOrientation.portraitDown, - // ]); - // } - final AppInitializer initializer = AppInitializer(); - - await initializer.initializeApp(); - await Future.delayed(Duration.zero); await EncryptionHelper.initialize(); - + if (Firebase.apps.isEmpty) { + await Firebase.initializeApp( + options: DefaultFirebaseOptions.currentPlatform); + } else { + Firebase.app(); + } + Get.put(FirebaseMessagesController()).getToken(); runApp(MyApp()); } @@ -49,7 +36,7 @@ class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return GetMaterialApp( - title: 'Sefer Service'.tr, + title: 'Inatleq Service'.tr, debugShowCheckedModeBanner: false, translations: MyTranslation(), locale: localController.language, diff --git a/lib/views/home/main.dart b/lib/views/home/main.dart index 7c1efe1..98e460b 100644 --- a/lib/views/home/main.dart +++ b/lib/views/home/main.dart @@ -12,465 +12,485 @@ import 'package:service/views/widgets/elevated_btn.dart'; import 'package:service/views/widgets/my_dialog.dart'; import 'package:service/views/widgets/my_textField.dart'; +import '../../constant/box_name.dart'; import '../../constant/style.dart'; import '../../controller/mainController/pages/add_car.dart'; -import '../../controller/mainController/pages/contact_page.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 '../../print.dart'; import '../widgets/my_scafold.dart'; +// --- Service Item Model --- +// A helper class to structure the data for each service card. +// This makes the code cleaner and easier to manage. +class ServiceItem { + final String title; + final IconData icon; + final VoidCallback onTap; + + ServiceItem({required this.title, required this.icon, required this.onTap}); +} + +// --- Main Screen Widget (Redesigned) --- class Main extends StatelessWidget { Main({super.key}); - MainController mainController = Get.put(MainController()); + + final MainController mainController = Get.put(MainController()); + @override Widget build(BuildContext context) { - return MyScaffold(title: 'Sefer Service'.tr, isleading: false, body: [ - ListView( - children: [ - InkWell( - onTap: () { - MyDialog().getDialog( - 'insert passenger phone'.tr, - 'midTitle', - Column( - children: [ - Form( - key: mainController.formKey, - child: MyTextForm( - controller: mainController.passengerPhoneController, - label: 'insert passenger phone'.tr, - hint: 'insert passenger phone'.tr, - type: TextInputType.phone, - )), - ], - ), - () { - mainController.searchPassengerByPhone(); - }, - ); - }, - child: Container( - decoration: AppStyle.boxDecoration, - child: Padding( - padding: const EdgeInsets.all(8.0), - child: Text( - 'passenger details by phone'.tr, - style: AppStyle.title, - textAlign: TextAlign.center, + // --- List of Services --- + // All services are defined here in a list. This makes it easy to add, remove, or reorder them. + // The original onTap logic is preserved exactly as it was. + final List services = [ + ServiceItem( + title: 'passenger details by phone'.tr, + icon: Icons.person_search_rounded, + onTap: () { + MyDialog().getDialog( + 'insert passenger phone'.tr, + 'midTitle', + Column(children: [ + Form( + key: mainController.formKey, + child: MyTextForm( + controller: mainController.passengerPhoneController, + label: 'insert passenger phone'.tr, + hint: 'insert passenger phone'.tr, + type: TextInputType.phone, ), ), - ), - ), - const SizedBox( - height: 20, - ), - InkWell( - onTap: () { - MyDialog().getDialog( - 'insert Driver phone'.tr, - 'midTitle', - Column( - children: [ - Form( - key: mainController.formKey, - child: MyTextForm( - controller: mainController.driverPhoneController, - label: 'insert Driver phone'.tr, - hint: 'insert Driver phone'.tr, - type: TextInputType.phone, - )), - ], - ), - () { - mainController.searchDriverByPhone(); - }, - ); - }, - child: Container( - decoration: AppStyle.boxDecoration, - child: Padding( - padding: const EdgeInsets.all(8.0), - child: Text( - 'Driver details by phone'.tr, - style: AppStyle.title, - textAlign: TextAlign.center, + ]), + () => mainController.searchPassengerByPhone(), + ); + }, + ), + ServiceItem( + title: 'Driver details by phone'.tr, + icon: Icons.support_agent_rounded, + onTap: () { + MyDialog().getDialog( + 'insert Driver phone'.tr, + 'midTitle', + Column(children: [ + Form( + key: mainController.formKey, + child: MyTextForm( + controller: mainController.driverPhoneController, + label: 'insert Driver phone'.tr, + hint: 'insert Driver phone'.tr, + type: TextInputType.phone, ), ), - ), - ), - const SizedBox( - height: 20, - ), - InkWell( - onTap: () async { - await mainController.getDriverNotCompleteRegistration(); - Get.to(() => DriversCantRegister()); - }, - child: Container( - decoration: AppStyle.boxDecoration, - child: Padding( - padding: const EdgeInsets.all(8.0), - child: Text( - 'Drivers Cant Register'.tr, - style: AppStyle.title, - textAlign: TextAlign.center, + ]), + () => mainController.searchDriverByPhone(), + ); + }, + ), + ServiceItem( + title: 'Driver details by national number'.tr, + icon: Icons.support_agent_rounded, + onTap: () { + MyDialog().getDialog( + 'insert Driver national'.tr, + 'midTitle', + Column(children: [ + Form( + key: mainController.formKey, + child: MyTextForm( + controller: mainController.driverPhoneController, + label: 'insert Driver national'.tr, + hint: 'insert Driver national'.tr, + type: TextInputType.number, ), ), + ]), + () => mainController.searchDriverByNational(), + ); + }, + ), + ServiceItem( + title: 'Drivers waitting Register'.tr, + icon: Icons.pending_actions_rounded, + onTap: () async { + await mainController.getDriverWantCompleteRegistration(); + Get.to(() => DriversCantRegister()); + }, + ), + ServiceItem( + title: 'Register new driver'.tr, + icon: Icons.person, + onTap: () { + // await mainController.getDriverWantCompleteRegistration(); + Get.to(() => RegisterCaptain()); + }, + ), + ServiceItem( + title: 'Drivers Cant Register'.tr, + icon: Icons.car_crash, + onTap: () async { + await mainController.getDriverNotCompleteRegistration(); + Get.to(() => DriversCantRegister()); + }, + ), + ServiceItem( + title: 'Drivers phones not register'.tr, + icon: Icons.person, + onTap: () async { + await mainController.getDriversPhoneNotComplete(); + Get.to(() => DriversCantRegister()); + }, + ), + ServiceItem( + title: 'Drivers Activity'.tr, + icon: Icons.person, + onTap: () async { + await mainController.getDriversPhoneNotComplete(); + Get.to(() => DriversCantRegister()); + }, + ), + ServiceItem( + title: 'Passengers Cant Register'.tr, + icon: Icons.group_off_rounded, + onTap: () async { + await mainController.getPassengerNotCompleteRegistration(); + Get.to(() => PassengersCantRegister()); + }, + ), + ServiceItem( + title: 'Add car'.tr, + icon: Icons.add_circle_outline_rounded, + onTap: () async { + await mainController.getdriverWithoutCar(); + if (mainController.driverWithoutCar.isNotEmpty) { + Get.to(() => const AddCar()); + } + }, + ), + ServiceItem( + title: 'Edit car plate'.tr, + icon: Icons.edit_note_rounded, + onTap: () async { + await mainController.getCarPlateNotEdit(); + if (mainController.carPlateNotEdit.isNotEmpty) { + Get.to(() => const EditCarPlate()); + } + }, + ), + ServiceItem( + title: "View complaint".tr, + icon: Icons.report_problem_rounded, + onTap: () { + Get.to(() => const Complaint()); + }, + ), + ServiceItem( + title: "Welcome call".tr, + icon: Icons.ring_volume_rounded, + onTap: () async { + await mainController.getNewDriverRegister(); + Get.to(() => const WelcomeCall()); + }, + ), + ServiceItem( + title: "best driver".tr, + icon: Icons.emoji_events_rounded, + onTap: () async { + await mainController.getNewDriverRegister(); + Get.to(() => DriverTheBest()); + }, + ), + ServiceItem( + title: "Add Driver Who Wants to Work".tr, + icon: Icons.person_add_alt_1_rounded, + onTap: () { + Get.defaultDialog( + barrierDismissible: false, + title: "Add Driver Who Wants to Work".tr, + content: SizedBox( + width: Get.width * .7, + height: 300, + child: Form( + key: mainController.formKey, + child: ListView( + 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: 'Insert license type of Driver'.tr, + type: TextInputType.number), + ], + )), ), - ), - const SizedBox( - height: 20, - ), - InkWell( - onTap: () async { - await mainController.getPassengerNotCompleteRegistration(); - Get.to(() => PassengersCantRegister()); - }, - child: Container( - decoration: AppStyle.boxDecoration, - child: Padding( - padding: const EdgeInsets.all(8.0), - child: Text( - 'Passengers Cant Register'.tr, - style: AppStyle.title, - textAlign: TextAlign.center, - ), - ), + confirm: MyElevatedButton( + title: 'Add'.tr, + onPressed: () async { + // if (mainController.formKey.currentState!.validate()) { + 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') { + Get.back(); + mainController.driverNameController.clear(); + mainController.nationalIdController.clear(); + mainController.birthDateController.clear(); + mainController.licenseTypeController.clear(); + mainController.siteDriverController.clear(); + mainController.phoneController.clear(); + Get.snackbar('done', '', + backgroundColor: AppColor.greenColor); + } + // } + }, + kolor: AppColor.greenColor, ), - ), - const SizedBox( - height: 20, - ), - InkWell( - onTap: () async { - await mainController.getdriverWithoutCar(); - if (mainController.driverWithoutCar.isNotEmpty) { - Get.to(() => const AddCar()); - } - }, - child: Container( - decoration: AppStyle.boxDecoration, - child: Padding( - padding: const EdgeInsets.all(8.0), - child: Text( - 'Add car'.tr, - style: AppStyle.title, - textAlign: TextAlign.center, - ), - ), + cancel: MyElevatedButton( + title: 'Cancel'.tr, + kolor: AppColor.redColor, + onPressed: () { + Get.back(); + }), + ); + }, + ), + ServiceItem( + title: "Add Car Who Wants to Work".tr, + icon: Icons.directions_car_filled_rounded, + onTap: () { + Get.defaultDialog( + barrierDismissible: false, + title: "Add Car Who Wants to Work".tr, + content: SizedBox( + width: Get.width * .7, + height: 300, + child: Form( + key: mainController.formKey, + child: ListView( + 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: 'Insert registration_date of Car'.tr, + type: TextInputType.datetime), + ], + )), ), - ), - InkWell( - onTap: () async { - await mainController.getCarPlateNotEdit(); - if (mainController.carPlateNotEdit.isNotEmpty) { - Get.to(() => const EditCarPlate()); - } - }, - child: Container( - decoration: AppStyle.boxDecoration, - child: Padding( - padding: const EdgeInsets.all(8.0), - child: Text( - 'Edit car plate'.tr, - style: AppStyle.title, - textAlign: TextAlign.center, - ), - ), + confirm: MyElevatedButton( + title: 'Add'.tr, + onPressed: () async { + // if (mainController.formKey.currentState!.validate()) { + 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, + }); + Log.print('res: ${res}'); + if (res != 'failure') { + Get.back(); + mainController.ownerController.clear(); + mainController.carNumberController.clear(); + mainController.manufactureYearController.clear(); + mainController.carModelController.clear(); + mainController.siteCarController.clear(); + mainController.carTypeController.clear(); + mainController.registrationDateController.clear(); + mainController.phoneController.clear(); + Get.snackbar('done', '', + backgroundColor: AppColor.greenColor); + } + // } + }, + kolor: AppColor.greenColor, ), + cancel: MyElevatedButton( + title: 'Cancel'.tr, + kolor: AppColor.redColor, + onPressed: () { + Get.back(); + }), + ); + }, + ), + ]; + + // --- Building the UI --- + return MyScaffold( + title: 'Intaleq Service'.tr, + isleading: false, + action: MyElevatedButton( + title: 'Logout'.tr, + onPressed: () async { + // box.write(BoxName.employeename, 'masa'); + print(box.read(BoxName.employeename).toString()); + }, + kolor: AppColor.redColor, + ), + // The body now uses a GridView for a better layout. + // You can replace the color with your main theme color. + // backgroundColor: const Color(0xFFF5F7FA), + body: [ + GridView.builder( + padding: const EdgeInsets.all(16.0), + itemCount: services.length, + gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: 2, // Two columns + crossAxisSpacing: 16.0, // Horizontal space between cards + mainAxisSpacing: 16.0, // Vertical space between cards + childAspectRatio: 1.1, // Adjust card shape (width/height ratio) ), - const SizedBox( - height: 20, - ), - InkWell( - onTap: () async { - // await mainController.getCarPlateNotEdit(); - // if (mainController.carPlateNotEdit.isNotEmpty) { - Get.to(() => const Complaint()); - // } - }, - child: Container( - decoration: AppStyle.boxDecoration, - child: Padding( - padding: const EdgeInsets.all(8.0), - child: Text( - "View complaint".tr, - style: AppStyle.title, - textAlign: TextAlign.center, - ), - ), - ), - ), - const SizedBox( - height: 20, - ), - InkWell( - onTap: () async { - await mainController.getNewDriverRegister(); - // if (mainController.carPlateNotEdit.isNotEmpty) { - Get.to(() => const WelcomeCall()); - // } - }, - child: Container( - decoration: AppStyle.boxDecoration, - child: Padding( - padding: const EdgeInsets.all(8.0), - child: Text( - "Welcome call".tr, - style: AppStyle.title, - textAlign: TextAlign.center, - ), - ), - ), - ), - const SizedBox( - height: 20, - ), - InkWell( - onTap: () async { - await mainController.getNewDriverRegister(); - // if (mainController.carPlateNotEdit.isNotEmpty) { - Get.to(() => DriverTheBest()); - // } - }, - child: Container( - decoration: AppStyle.boxDecoration, - child: Padding( - padding: const EdgeInsets.all(8.0), - child: Text( - "best driver".tr, - style: AppStyle.title, - textAlign: TextAlign.center, - ), - ), - ), - ), - const SizedBox( - height: 20, - ), - InkWell( - onTap: () async { - Get.defaultDialog( - barrierDismissible: false, - title: "Add Driver Who Wants to Work".tr, - content: SizedBox( - width: Get.width * .7, - height: 300, - child: Form( - key: mainController.formKey, - child: ListView( - 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: 'Insert license type of Driver'.tr, - type: TextInputType.number), - ], - )), - ), - confirm: MyElevatedButton( - title: 'Add'.tr, - onPressed: () async { - if (mainController.formKey.currentState!.validate()) { - 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') { - Get.back(); - mainController.driverNameController.clear(); - mainController.nationalIdController.clear(); - mainController.birthDateController.clear(); - mainController.licenseTypeController.clear(); - mainController.siteDriverController.clear(); - mainController.phoneController.clear(); - Get.snackbar('done', '', - backgroundColor: AppColor.greenColor); - } - } - }, - kolor: AppColor.greenColor, - ), - cancel: MyElevatedButton( - title: 'Cancel'.tr, - kolor: AppColor.redColor, - onPressed: () { - Get.back(); - }), - ); - }, - child: Container( - decoration: AppStyle.boxDecoration, - child: Padding( - padding: const EdgeInsets.all(8.0), - child: Text( - "Add Driver Who Wants to Work".tr, - style: AppStyle.title, - textAlign: TextAlign.center, - ), - ), - ), - ), - const SizedBox( - height: 20, - ), - InkWell( - onTap: () async { - Get.defaultDialog( - barrierDismissible: false, - title: "Add Car Who Wants to Work".tr, - content: SizedBox( - width: Get.width * .7, - height: 300, - child: Form( - key: mainController.formKey, - child: ListView( - 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: 'Insert registration_date of Car'.tr, - type: TextInputType.datetime), - ], - )), - ), - confirm: MyElevatedButton( - title: 'Add'.tr, - onPressed: () async { - if (mainController.formKey.currentState!.validate()) { - 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') { - Get.back(); - mainController.ownerController.clear(); - mainController.carNumberController.clear(); - mainController.manufactureYearController.clear(); - mainController.carModelController.clear(); - mainController.siteCarController.clear(); - mainController.carTypeController.clear(); - mainController.registrationDateController.clear(); - mainController.phoneController.clear(); - Get.snackbar('done', '', - backgroundColor: AppColor.greenColor); - } - } - }, - kolor: AppColor.greenColor, - ), - cancel: MyElevatedButton( - title: 'Cancel'.tr, - kolor: AppColor.redColor, - onPressed: () { - Get.back(); - }), - ); - }, - child: Container( - decoration: AppStyle.boxDecoration, - child: Padding( - padding: const EdgeInsets.all(8.0), - child: Text( - "Add Car Who Wants to Work".tr, - style: AppStyle.title, - textAlign: TextAlign.center, - ), - ), - ), - ), - const SizedBox( - height: 20, - ), - // GestureDetector( - // onTap: () { - // Get.to(() => const ContactPage()); - // }, - // child: Container( - // decoration: AppStyle.boxDecoration, - // child: const Center(child: Text('contact')), - // ), - // ) - ], - ) - ]); + itemBuilder: (context, index) { + final service = services[index]; + return ServiceCard( + title: service.title, + icon: service.icon, + onTap: service.onTap, + ); + }, + ), + ], + ); + } +} + +// --- Service Card Widget --- +// A reusable widget for displaying each service. +class ServiceCard extends StatelessWidget { + final String title; + final IconData icon; + final VoidCallback onTap; + + const ServiceCard({ + super.key, + required this.title, + required this.icon, + required this.onTap, + }); + + @override + Widget build(BuildContext context) { + return InkWell( + onTap: onTap, + borderRadius: BorderRadius.circular(16.0), + child: Container( + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(16.0), + boxShadow: [ + BoxShadow( + color: Colors.grey.withOpacity(0.15), + spreadRadius: 2, + blurRadius: 8, + offset: const Offset(0, 4), // changes position of shadow + ), + ], + ), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + // Icon + Icon( + icon, + size: 48.0, + // You can replace this color with your AppStyle color + color: Theme.of(context).primaryColor, + ), + const SizedBox(height: 12), + // Title + Padding( + padding: const EdgeInsets.symmetric(horizontal: 8.0), + child: Text( + title, + textAlign: TextAlign.center, + style: const TextStyle( + fontWeight: FontWeight.w600, + fontSize: 14, + // You can replace this color with your AppStyle color + color: Color(0xFF4A4A4A), + ), + maxLines: 2, + overflow: TextOverflow.ellipsis, + ), + ), + ], + ), + ), + ); } } diff --git a/lib/views/widgets/my_scafold.dart b/lib/views/widgets/my_scafold.dart index bdee504..d25384f 100644 --- a/lib/views/widgets/my_scafold.dart +++ b/lib/views/widgets/my_scafold.dart @@ -42,7 +42,8 @@ class MyScaffold extends StatelessWidget { actions: [action], title: Text( title, - style: AppStyle.title.copyWith(fontSize: 30), + style: AppStyle.title + .copyWith(fontSize: 20, fontWeight: FontWeight.bold), ), ), body: SafeArea(child: Stack(children: body))); diff --git a/lib/views/widgets/my_textField.dart b/lib/views/widgets/my_textField.dart index dd23c81..4e589e2 100644 --- a/lib/views/widgets/my_textField.dart +++ b/lib/views/widgets/my_textField.dart @@ -55,11 +55,7 @@ class MyTextForm extends StatelessWidget { return 'Please enter a valid email.'.tr; } } else if (type == TextInputType.phone) { - if (box.read(BoxName.countryCode) == 'Egypt') { - if (value.length != 11) { - return 'Please enter a valid phone number.'.tr; - } - } else if (value.length != 11) { + if (value.length > 14) { //for this you will return to 10 but now for service egypt return 'Please enter a valid phone number.'.tr; } diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index 7300adc..87f6847 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -11,6 +11,7 @@ import firebase_auth import firebase_core import firebase_messaging import flutter_image_compress_macos +import flutter_local_notifications import flutter_secure_storage_macos import google_sign_in_ios import path_provider_foundation @@ -25,6 +26,7 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { FLTFirebaseCorePlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseCorePlugin")) FLTFirebaseMessagingPlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseMessagingPlugin")) FlutterImageCompressMacosPlugin.register(with: registry.registrar(forPlugin: "FlutterImageCompressMacosPlugin")) + FlutterLocalNotificationsPlugin.register(with: registry.registrar(forPlugin: "FlutterLocalNotificationsPlugin")) FlutterSecureStoragePlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStoragePlugin")) FLTGoogleSignInPlugin.register(with: registry.registrar(forPlugin: "FLTGoogleSignInPlugin")) PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) diff --git a/pubspec.lock b/pubspec.lock index df95cd8..62c4287 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -217,6 +217,14 @@ packages: url: "https://pub.dev" source: hosted version: "3.1.1" + dbus: + dependency: transitive + description: + name: dbus + sha256: "79e0c23480ff85dc68de79e2cd6334add97e48f7f4865d17686dd6ea81a47e8c" + url: "https://pub.dev" + source: hosted + version: "0.7.11" device_info_plus: dependency: transitive description: @@ -470,6 +478,38 @@ packages: url: "https://pub.dev" source: hosted version: "6.0.0" + flutter_local_notifications: + dependency: "direct main" + description: + name: flutter_local_notifications + sha256: "19ffb0a8bb7407875555e5e98d7343a633bb73707bae6c6a5f37c90014077875" + url: "https://pub.dev" + source: hosted + version: "19.5.0" + flutter_local_notifications_linux: + dependency: transitive + description: + name: flutter_local_notifications_linux + sha256: e3c277b2daab8e36ac5a6820536668d07e83851aeeb79c446e525a70710770a5 + url: "https://pub.dev" + source: hosted + version: "6.0.0" + flutter_local_notifications_platform_interface: + dependency: transitive + description: + name: flutter_local_notifications_platform_interface + sha256: "277d25d960c15674ce78ca97f57d0bae2ee401c844b6ac80fcd972a9c99d09fe" + url: "https://pub.dev" + source: hosted + version: "9.1.0" + flutter_local_notifications_windows: + dependency: transitive + description: + name: flutter_local_notifications_windows + sha256: "8d658f0d367c48bd420e7cf2d26655e2d1130147bca1eea917e576ca76668aaf" + url: "https://pub.dev" + source: hosted + version: "1.0.3" flutter_plugin_android_lifecycle: dependency: transitive description: @@ -1212,6 +1252,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.7.4" + timezone: + dependency: transitive + description: + name: timezone + sha256: dd14a3b83cfd7cb19e7888f1cbc20f258b8d71b54c06f79ac585f14093a287d1 + url: "https://pub.dev" + source: hosted + version: "0.10.1" timing: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 27ba7a8..66e433d 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -62,6 +62,7 @@ dependencies: jwt_decoder: ^2.0.1 encrypt: ^5.0.3 share_plus: ^7.2.1 + flutter_local_notifications: ^19.5.0 dev_dependencies: flutter_test: @@ -105,7 +106,7 @@ flutter_launcher_icons: # To add assets to your application, add an assets section, like this: assets: - - assets/images + - assets/images/ # An image asset can refer to one or more resolution-specific "variants", see # https://flutter.dev/assets-and-images/#resolution-aware diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake index 53ac29f..bc86d5b 100644 --- a/windows/flutter/generated_plugins.cmake +++ b/windows/flutter/generated_plugins.cmake @@ -13,6 +13,7 @@ list(APPEND FLUTTER_PLUGIN_LIST ) list(APPEND FLUTTER_FFI_PLUGIN_LIST + flutter_local_notifications_windows ) set(PLUGIN_BUNDLED_LIBRARIES)