Initial commit for service

This commit is contained in:
Hamza-Ayed
2026-01-20 23:36:57 +03:00
parent 66ae6c0ddb
commit ad2511bd96
34 changed files with 3757 additions and 4761 deletions

View File

@@ -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"
}
}
}

View File

@@ -1,54 +1,97 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<!-- ===== Permissions ===== -->
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"
<!-- التخزين القديم (مقيَّد حسب الإصدارات) -->
<uses-permission
android:name="android.permission.READ_EXTERNAL_STORAGE"
android:maxSdkVersion="32" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
<uses-permission
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="28" />
<uses-permission android:name="android.permission.CAMERA" />
<!-- الإذن الحديث للصور على 33+ -->
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.WRITE_CONTACTS" />
<!-- (اختياري) تنبيهات 13+ -->
<!-- <uses-permission android:name="android.permission.POST_NOTIFICATIONS" /> -->
<!-- ===== Visibility Queries (Android 11+) =====
تمكين اكتشاف التطبيقات/الأنشطة التي تتعامل مع السكيمز:
whatsapp://, http/https, tel, sms, mailto -->
<queries>
<!-- WhatsApp -->
<package android:name="com.whatsapp" />
<intent>
<action android:name="android.intent.action.VIEW" />
<data android:scheme="whatsapp" />
</intent>
<!-- متصفحات (http/https) -->
<intent>
<action android:name="android.intent.action.VIEW" />
<data android:scheme="https" />
</intent>
<intent>
<action android:name="android.intent.action.VIEW" />
<data android:scheme="http" />
</intent>
<!-- هاتف / SMS / بريد -->
<intent>
<action android:name="android.intent.action.DIAL" />
<data android:scheme="tel" />
</intent>
<intent>
<action android:name="android.intent.action.SENDTO" />
<data android:scheme="smsto" />
</intent>
<intent>
<action android:name="android.intent.action.SENDTO" />
<data android:scheme="mailto" />
</intent>
<!-- تستخدمه إضافات Flutter للنصوص -->
<intent>
<action android:name="android.intent.action.PROCESS_TEXT" />
<data android:mimeType="text/plain" />
</intent>
</queries>
<!-- ===== Application ===== -->
<application
android:label="service"
android:name="${applicationName}"
android:label="service"
android:icon="@mipmap/launcher_icon">
<activity
android:name=".MainActivity"
android:exported="true"
android:launchMode="singleTop"
android:taskAffinity=""
android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize">
<!-- Specifies an Android theme to apply to this Activity as soon as
the Android process has started. This theme is visible to the user
while the Flutter UI initializes. After that, this theme continues
to determine the Window background behind the Flutter UI. -->
android:windowSoftInputMode="adjustResize"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode">
<!-- Theme أثناء الإقلاع -->
<meta-data
android:name="io.flutter.embedding.android.NormalTheme"
android:resource="@style/NormalTheme"
/>
android:resource="@style/NormalTheme" />
<!-- نقطة دخول التطبيق -->
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<!-- Don't delete the meta-data below.
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
<!-- مطلوب لتسجيل البلَغنز -->
<meta-data
android:name="flutterEmbedding"
android:value="2" />
</application>
<!-- Required to query activities that can process text, see:
https://developer.android.com/training/package-visibility and
https://developer.android.com/reference/android/content/Intent#ACTION_PROCESS_TEXT.
In particular, this is used by the Flutter engine in io.flutter.plugin.text.ProcessTextPlugin. -->
<queries>
<intent>
<action android:name="android.intent.action.PROCESS_TEXT" />
<data android:mimeType="text/plain" />
</intent>
</queries>
</manifest>

View File

@@ -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'

297
ios/Podfile.lock Normal file
View File

@@ -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

View File

@@ -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 = "<group>"; };
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
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 = "<group>"; };
331C807B294A618700263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = "<group>"; };
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 = "<group>"; };
@@ -56,6 +60,12 @@
97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
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 = "<group>"; };
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 = "<group>"; };
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 = "<group>"; };
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 = "<group>"; };
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 = "<group>"; };
F533C517F5928BDCFDC9295F /* GoogleService-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; name = "GoogleService-Info.plist"; path = "Runner/GoogleService-Info.plist"; sourceTree = "<group>"; };
/* 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 = "<group>";
};
3AF11E78240F2F4936A829ED /* Frameworks */ = {
isa = PBXGroup;
children = (
D353C14D406BEA76DDC4CEDD /* Pods_Runner.framework */,
1A63E609133A89EEF042220B /* Pods_RunnerTests.framework */,
);
name = Frameworks;
sourceTree = "<group>";
};
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 = "<group>";
};
9740EEB11CF90186004384FC /* Flutter */ = {
isa = PBXGroup;
children = (
@@ -97,6 +139,8 @@
97C146EF1CF9000F007C117D /* Products */,
331C8082294A63A400263BE5 /* RunnerTests */,
F533C517F5928BDCFDC9295F /* GoogleService-Info.plist */,
92F26F512BB0C437BB6F8B84 /* Pods */,
3AF11E78240F2F4936A829ED /* Frameworks */,
);
sourceTree = "<group>";
};
@@ -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;

View File

@@ -4,4 +4,7 @@
<FileRef
location = "group:Runner.xcodeproj">
</FileRef>
<FileRef
location = "group:Pods/Pods.xcodeproj">
</FileRef>
</Workspace>

View File

@@ -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";

View File

@@ -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 =

View File

@@ -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<String> tokens = [];
List dataTokens = [];
late String driverID;
late String driverToken;
NotificationSettings? notificationSettings;
Future<void> getNotificationSettings() async {
// Get the current notification settings
NotificationSettings? notificationSettings =
await FirebaseMessaging.instance.getNotificationSettings();
'Notification authorization status: ${notificationSettings.authorizationStatus}';
// Call the update function if needed
update();
}
Future<void> requestFirebaseMessagingPermission() async {
FirebaseMessaging messaging = FirebaseMessaging.instance;
// Check if the platform is Android
if (Platform.isAndroid) {
// Request permission for Android
await messaging.requestPermission();
} else if (Platform.isIOS) {
// Request permission for iOS
NotificationSettings settings = await messaging.requestPermission(
alert: true,
announcement: true,
badge: true,
carPlay: true,
criticalAlert: true,
provisional: false,
sound: true,
);
messaging.setForegroundNotificationPresentationOptions(
alert: true, badge: true, sound: true);
}
}
// NotificationController notificationController =
// Get.isRegistered<NotificationController>()
// ? Get.find<NotificationController>()
// : Get.put(NotificationController());
Future getToken() async {
fcmToken.getToken().then((token) {
// Log.print('fcmToken: ${token}');
box.write(BoxName.tokenFCM, (token.toString()));
});
// 🔹 الاشتراك في topic
await fcmToken.subscribeToTopic("service"); // أو "users" حسب نوع المستخدم
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<void> fireBaseTitles(RemoteMessage message) async {
// [!! تعديل !!]
// اقرأ "النوع" من حمولة البيانات، وليس من العنوان
String category = message.data['category'] ?? '';
// اقرأ العنوان (للعرض)
String title = message.notification?.title ?? '';
String body = message.notification?.body ?? '';
if (category == 'new_service_request') {
// <-- مثال: كان 'Order'.tr
Log.print('message: ${message}');
if (Platform.isAndroid) {
NotificationController().showNotification(title, body, 'Order');
}
}
}
}

View File

@@ -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<dynamic> 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;

View File

@@ -15,19 +15,19 @@ import '../../print.dart';
class AppInitializer {
List<Map<String, dynamic>> links = [];
Future<void> initializeApp() async {
if (box.read(BoxName.jwt) == null) {
await CRUD().getJWT();
} else {
bool isTokenExpired = JwtDecoder.isExpired(X
.r(X.r(X.r(box.read(BoxName.jwt), cn), cC), cs)
.toString()
.split(AppInformation.addd)[0]);
if (isTokenExpired) {
await CRUD().getJWT();
}
}
}
// Future<void> initializeApp() async {
// if (box.read(BoxName.jwt) == null) {
// await CRUD().getJWT();
// } else {
// bool isTokenExpired = JwtDecoder.isExpired(X
// .r(X.r(X.r(box.read(BoxName.jwt), cn), cC), cs)
// .toString()
// .split(AppInformation.addd)[0]);
// if (isTokenExpired) {
// await CRUD().getJWT();
// }
// }
// }
getAIKey(String key1) async {
if (box.read(BoxName.firstTimeLoadKey) == null) {
@@ -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 {}

View File

@@ -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": "تاريخ الإصدار",

View File

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

View File

@@ -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();
}
}

View File

@@ -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<void> makePhoneCall(String phoneNumber) async {
final Uri launchUri = Uri(
scheme: 'tel',
@@ -79,25 +101,29 @@ class MainController extends GetxController {
await launchUrl(launchUri);
}
void launchCommunication(
Future<void> 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);
}
}
}

View File

@@ -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,

View File

@@ -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<MainController>();
@override
Widget build(BuildContext context) {
return Scaffold(
body: GetBuilder<MainController>(builder: (mainController) {
Map data = mainController.driverData['message'][0];
return CupertinoPageScaffold(
navigationBar: CupertinoNavigationBar(
middle: Text('${data['first_name']} ${data['last_name']}'),
@@ -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<String, dynamic> 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<String, dynamic> 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<String, dynamic> 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<String, dynamic> 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<String, dynamic> 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),
],
);
}
}

View File

@@ -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<MainController>(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<void>(
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<Widget>.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),
),
),
],
),
),
],
),
);
},
),
);
},
),
),
],
);

View File

@@ -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<Widget> formFields,
}) {
return SingleChildScrollView(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
// ClipRRect(
// borderRadius: BorderRadius.circular(12),
// child: Image.network(
// imageUrl.value,
// fit: BoxFit.fitWidth,
// errorBuilder: (context, error, stackTrace) {
// return Container(
// height: 200,
// color: Colors.grey[200],
// child: Center(
// child: Text(
// 'Image not available'.tr,
// style: const TextStyle(color: Colors.grey),
// ),
// ),
// );
// },
// ),
// ),
// const SizedBox(height: 20),
const Divider(),
...formFields,
],
),
);
}
// <-- MODIFICADO: Usa RegisterCaptainServiceController -->
Widget _buildColorDropdown(RegisterCaptainServiceController controller) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 8.0),
child: Obx(
() => DropdownButtonFormField<String>(
value: controller.colorHex.value.isEmpty
? null
: controller.colorHex.value,
isExpanded: true,
decoration: InputDecoration(
labelText: 'Car Color'.tr,
prefixIcon: Icon(Icons.color_lens, color: AppColor.secondaryColor),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
),
),
// <-- MODIFICADO: Usa la referencia estática del nuevo controlador -->
items: RegisterCaptainServiceController.kCarColorOptions.map((opt) {
final hex = opt['hex']!;
final key = opt['key']!;
return DropdownMenuItem<String>(
value: hex,
child: Row(
children: [
Container(
width: 18,
height: 18,
decoration: BoxDecoration(
color: controller.hexToColor(hex),
shape: BoxShape.circle,
border: Border.all(color: Colors.black12),
),
),
const SizedBox(width: 12),
Expanded(child: Text(key.tr)),
],
),
);
}).toList(),
onChanged: (hex) {
if (hex != null) {
controller.updateColorSelection(hex);
}
},
),
),
);
}
// <-- MODIFICADO: Usa RegisterCaptainServiceController -->
Widget _buildGenderDropdown(RegisterCaptainServiceController controller) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 8.0),
child: Obx(
() => DropdownButtonFormField<String>(
value: controller.selectedGender.value.isEmpty
? null
: controller.selectedGender.value,
isExpanded: true,
decoration: InputDecoration(
labelText: 'Gender'.tr,
prefixIcon: Icon(Icons.wc, color: AppColor.secondaryColor),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
),
),
items: ['Male', 'Female'].map((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value.tr),
);
}).toList(),
onChanged: (String? newValue) {
if (newValue != null) {
controller.selectedGender.value = newValue;
}
},
),
),
);
}
// <-- MODIFICADO: Usa RegisterCaptainServiceController -->
Widget _buildFuelDropdown(RegisterCaptainServiceController controller) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 8.0),
child: Obx(
() => DropdownButtonFormField<String>(
// <-- MODIFICADO: Usa la referencia estática del nuevo controlador -->
value: RegisterCaptainServiceController.kFuelOptions
.contains(controller.selectedFuel.value)
? controller.selectedFuel.value
: null,
isExpanded: true,
decoration: InputDecoration(
labelText: 'Fuel Type'.tr,
prefixIcon:
Icon(Icons.local_gas_station, color: AppColor.secondaryColor),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
),
),
// <-- MODIFICADO: Usa la referencia estática del nuevo controlador -->
items:
RegisterCaptainServiceController.kFuelOptions.map((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value.tr),
);
}).toList(),
onChanged: (String? newValue) {
if (newValue != null) {
controller.selectedFuel.value = newValue;
}
},
),
),
);
}
// --- PAGE 1: Driver License Front ---
// <-- MODIFICADO: Usa RegisterCaptainServiceController -->
Widget _buildSyrianDriverLicenseFront(
BuildContext context, RegisterCaptainServiceController controller) {
return _buildPageContent(
imageUrl: controller.docUrls['driver_license_front']!,
formFields: [
Row(
children: [
Expanded(
child: _buildTextField(
label: 'First Name',
controller: controller.firstNameController,
keyboardType: TextInputType.name),
),
const SizedBox(width: 8),
Expanded(
child: _buildTextField(
label: 'Last Name',
controller: controller.lastNameController,
keyboardType: TextInputType.name),
),
],
),
_buildTextField(
label: 'Phone Number',
controller: controller.phoneController,
hintText: 'e.g., 963992952235',
icon: Icons.phone,
keyboardType: TextInputType.phone,
),
_buildTextField(
label: 'Place of Registration',
controller: controller.siteController,
icon: Icons.location_city),
Row(
children: [
Expanded(
child: _buildTextField(
label: 'National Number',
controller: controller.nationalNumberController,
keyboardType: TextInputType.number,
icon: Icons.fingerprint),
),
const SizedBox(width: 8),
Expanded(child: _buildGenderDropdown(controller)),
],
),
_buildTextField(
label: 'Birthdate',
controller: controller.birthdateController,
icon: Icons.cake,
onTap: () =>
controller.selectDate(context, controller.birthdateController)),
],
);
}
// --- PAGE 2: Driver License Back ---
// <-- MODIFICADO: Usa RegisterCaptainServiceController -->
Widget _buildSyrianDriverLicenseBack(
BuildContext context, RegisterCaptainServiceController controller) {
return _buildPageContent(
imageUrl: controller.docUrls['driver_license_back']!,
formFields: [
_buildTextField(
label: 'License Category',
controller: controller.licenseCategoriesController),
_buildTextField(
label: 'Expiry Date',
controller: controller.expiryDateController,
icon: Icons.event_busy,
onTap: () => controller.selectDate(
context, controller.expiryDateController)),
],
);
}
// --- PAGE 3: Car License Front ---
// <-- MODIFICADO: Usa RegisterCaptainServiceController -->
Widget _buildSyrianCarLicenseFront(
BuildContext context, RegisterCaptainServiceController controller) {
return _buildPageContent(
imageUrl: controller.docUrls['car_license_front']!,
formFields: [
_buildTextField(
label: 'Owner Name',
controller: controller.ownerController,
keyboardType: TextInputType.name,
icon: Icons.person_search),
Row(
children: [
Expanded(child: _buildColorDropdown(controller)),
const SizedBox(width: 8),
Expanded(
child: _buildTextField(
label: 'Car Plate',
controller: controller.carPlateController,
),
),
],
),
// _buildTextField(
// label: 'VIN',
// controller: controller.vinController,
// icon: Icons.confirmation_number),
Row(
children: [
Expanded(
child: _buildTextField(
label: 'License Issue Date',
controller: controller.licenseIssueDateController,
onTap: () => controller.selectDate(
context, controller.licenseIssueDateController))),
const SizedBox(width: 8),
Expanded(
child: _buildTextField(
label: 'License Expiry Date',
controller: controller.carLicenseExpiryDateController,
onTap: () => controller.selectDate(
context, controller.carLicenseExpiryDateController))),
],
),
],
);
}
// --- PAGE 4: Car License Back ---
// <-- MODIFICADO: Usa RegisterCaptainServiceController -->
Widget _buildSyrianCarLicenseBack(
RegisterCaptainServiceController controller) {
return _buildPageContent(
imageUrl: controller.docUrls['car_license_back']!,
formFields: [
Row(
children: [
Expanded(
child: _buildTextField(
label: 'Make',
controller: controller.makeController,
)),
const SizedBox(width: 8),
Expanded(
child: _buildTextField(
label: 'Model',
controller: controller.modelController,
)),
],
),
Row(
children: [
Expanded(
child: _buildTextField(
label: 'Year',
controller: controller.yearController,
keyboardType: TextInputType.number)),
const SizedBox(width: 8),
Expanded(child: _buildFuelDropdown(controller)),
],
),
],
);
}
}
class StepIndicator extends StatelessWidget {
final int currentStep;
final int totalSteps;
const StepIndicator({
super.key,
required this.currentStep,
required this.totalSteps,
});
@override
Widget build(BuildContext context) {
return Column(
children: [
Text(
'${'Step'.tr} ${currentStep + 1} ${'of'.tr} $totalSteps',
style: const TextStyle(
fontSize: 18, fontWeight: FontWeight.bold, color: Colors.black54),
),
const SizedBox(height: 8),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: List.generate(totalSteps, (index) {
return Container(
margin: const EdgeInsets.symmetric(horizontal: 4),
width: 30,
height: 8,
decoration: BoxDecoration(
color: index <= currentStep
? AppColor.primaryColor
: Colors.grey.shade300,
borderRadius: BorderRadius.circular(4),
),
);
}),
),
],
);
}
}

View File

@@ -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,
),

View File

@@ -9,92 +9,109 @@ class PassengersPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return GetBuilder<MainController>(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<MainController>(
builder: (mainController) {
final data = _extractPassengerData(mainController);
return MyScaffold(
title: data != null
? '${data['first_name'] ?? ''} ${data['last_name'] ?? ''}'
: 'Passenger Not Found'.tr,
isleading: true,
body: [
if (data != null)
ListView(
children: [
_buildPersonalInfoCard(data),
_buildLatestRideCard(data),
_buildWalletInfoCard(data),
],
)
else
const Center(
child: Padding(
padding: EdgeInsets.all(24.0),
child: Text(
'No passenger data available.',
style: TextStyle(fontSize: 18, fontWeight: FontWeight.w500),
),
),
),
],
);
},
);
}
/// Helper method to extract the passenger data safely.
Map? _extractPassengerData(MainController controller) {
final passengerData = controller.passengerData;
if (passengerData == null ||
passengerData['message'] == null ||
passengerData['message'].isEmpty) {
return null;
}
return passengerData['message'][0];
}
Widget _buildPersonalInfoCard(Map data) {
return 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<Widget> 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)),
],
),
);

View File

@@ -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<MainController>(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<String, dynamic> driver;
const DriverCard({super.key, required this.driver});
Widget buildActionButton({
required IconData icon,
required Color color,
required VoidCallback onPressed,
}) {
return CupertinoButton(
padding: const EdgeInsets.symmetric(horizontal: 6.0),
onPressed: onPressed,
child: Icon(icon, color: color, size: 28),
);
}
@override
Widget build(BuildContext context) {
final controller = Get.find<MainController>();
return CupertinoCard(
margin: const EdgeInsets.all(16.0),
child: Padding(
@@ -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<MainController>().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<MainController>()
.makePhoneCall(driver['phone'].toString());
})),
CupertinoButton(
onPressed: () async {
await Get.find<MainController>().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<MainController>().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,
),
);

View File

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

View File

@@ -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';

View File

@@ -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<Future> 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,

View File

@@ -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<ServiceItem> 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,
),
),
],
),
),
);
}
}

View File

@@ -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)));

View File

@@ -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;
}

View File

@@ -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"))

View File

@@ -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:

View File

@@ -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

View File

@@ -13,6 +13,7 @@ list(APPEND FLUTTER_PLUGIN_LIST
)
list(APPEND FLUTTER_FFI_PLUGIN_LIST
flutter_local_notifications_windows
)
set(PLUGIN_BUNDLED_LIBRARIES)