diff --git a/.flutter-plugins-dependencies b/.flutter-plugins-dependencies index 544d02f..992e919 100644 --- a/.flutter-plugins-dependencies +++ b/.flutter-plugins-dependencies @@ -1 +1 @@ -{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"connectivity_plus","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/connectivity_plus-6.1.5/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"device_info_plus","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/device_info_plus-11.5.0/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"file_picker","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/file_picker-10.3.7/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"firebase_auth","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/firebase_auth-6.1.2/","native_build":true,"dependencies":["firebase_core"],"dev_dependency":false},{"name":"firebase_core","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/firebase_core-4.2.1/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"firebase_messaging","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/firebase_messaging-16.0.4/","native_build":true,"dependencies":["firebase_core"],"dev_dependency":false},{"name":"flutter_secure_storage","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/flutter_secure_storage-9.2.4/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"fluttertoast","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/fluttertoast-9.0.0/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"google_sign_in_ios","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/google_sign_in_ios-6.2.1/","shared_darwin_source":true,"native_build":true,"dependencies":[],"dev_dependency":false},{"name":"local_auth_darwin","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/local_auth_darwin-1.6.0/","shared_darwin_source":true,"native_build":true,"dependencies":[],"dev_dependency":false},{"name":"open_file_ios","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/open_file_ios-1.0.3/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"package_info_plus","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/package_info_plus-8.3.1/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"path_provider_foundation","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/path_provider_foundation-2.4.2/","shared_darwin_source":true,"native_build":true,"dependencies":[],"dev_dependency":false},{"name":"pdf_render","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/pdf_render-1.4.12/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"shared_preferences_foundation","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/shared_preferences_foundation-2.5.4/","shared_darwin_source":true,"native_build":true,"dependencies":[],"dev_dependency":false},{"name":"syncfusion_flutter_pdfviewer","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/syncfusion_flutter_pdfviewer-31.1.19/","native_build":true,"dependencies":["device_info_plus"],"dev_dependency":false},{"name":"url_launcher_ios","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/url_launcher_ios-6.3.4/","native_build":true,"dependencies":[],"dev_dependency":false}],"android":[{"name":"connectivity_plus","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/connectivity_plus-6.1.5/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"device_info_plus","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/device_info_plus-11.5.0/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"file_picker","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/file_picker-10.3.7/","native_build":true,"dependencies":["flutter_plugin_android_lifecycle"],"dev_dependency":false},{"name":"firebase_auth","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/firebase_auth-6.1.2/","native_build":true,"dependencies":["firebase_core"],"dev_dependency":false},{"name":"firebase_core","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/firebase_core-4.2.1/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"firebase_messaging","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/firebase_messaging-16.0.4/","native_build":true,"dependencies":["firebase_core"],"dev_dependency":false},{"name":"flutter_plugin_android_lifecycle","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/flutter_plugin_android_lifecycle-2.0.31/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"flutter_secure_storage","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/flutter_secure_storage-9.2.4/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"fluttertoast","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/fluttertoast-9.0.0/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"google_sign_in_android","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/google_sign_in_android-7.2.1/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"local_auth_android","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/local_auth_android-1.0.52/","native_build":true,"dependencies":["flutter_plugin_android_lifecycle"],"dev_dependency":false},{"name":"open_file_android","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/open_file_android-1.0.6/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"package_info_plus","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/package_info_plus-8.3.1/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"path_provider_android","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/path_provider_android-2.2.19/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"pdf_render","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/pdf_render-1.4.12/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"shared_preferences_android","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/shared_preferences_android-2.4.13/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"syncfusion_flutter_pdfviewer","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/syncfusion_flutter_pdfviewer-31.1.19/","native_build":true,"dependencies":["device_info_plus"],"dev_dependency":false},{"name":"url_launcher_android","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/url_launcher_android-6.3.20/","native_build":true,"dependencies":[],"dev_dependency":false}],"macos":[{"name":"connectivity_plus","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/connectivity_plus-6.1.5/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"device_info_plus","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/device_info_plus-11.5.0/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"file_picker","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/file_picker-10.3.7/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"firebase_auth","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/firebase_auth-6.1.2/","native_build":true,"dependencies":["firebase_core"],"dev_dependency":false},{"name":"firebase_core","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/firebase_core-4.2.1/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"firebase_messaging","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/firebase_messaging-16.0.4/","native_build":true,"dependencies":["firebase_core"],"dev_dependency":false},{"name":"flutter_secure_storage_macos","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/flutter_secure_storage_macos-3.1.3/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"google_sign_in_ios","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/google_sign_in_ios-6.2.1/","shared_darwin_source":true,"native_build":true,"dependencies":[],"dev_dependency":false},{"name":"local_auth_darwin","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/local_auth_darwin-1.6.0/","shared_darwin_source":true,"native_build":true,"dependencies":[],"dev_dependency":false},{"name":"open_file_mac","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/open_file_mac-1.0.3/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"package_info_plus","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/package_info_plus-8.3.1/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"path_provider_foundation","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/path_provider_foundation-2.4.2/","shared_darwin_source":true,"native_build":true,"dependencies":[],"dev_dependency":false},{"name":"pdf_render","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/pdf_render-1.4.12/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"shared_preferences_foundation","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/shared_preferences_foundation-2.5.4/","shared_darwin_source":true,"native_build":true,"dependencies":[],"dev_dependency":false},{"name":"syncfusion_pdfviewer_macos","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/syncfusion_pdfviewer_macos-31.1.19/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"url_launcher_macos","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/url_launcher_macos-3.2.3/","native_build":true,"dependencies":[],"dev_dependency":false}],"linux":[{"name":"connectivity_plus","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/connectivity_plus-6.1.5/","native_build":false,"dependencies":[],"dev_dependency":false},{"name":"device_info_plus","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/device_info_plus-11.5.0/","native_build":false,"dependencies":[],"dev_dependency":false},{"name":"file_picker","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/file_picker-10.3.7/","native_build":false,"dependencies":[],"dev_dependency":false},{"name":"flutter_secure_storage_linux","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/flutter_secure_storage_linux-1.2.3/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"open_file_linux","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/open_file_linux-0.0.5/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"package_info_plus","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/package_info_plus-8.3.1/","native_build":false,"dependencies":[],"dev_dependency":false},{"name":"path_provider_linux","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/path_provider_linux-2.2.1/","native_build":false,"dependencies":[],"dev_dependency":false},{"name":"shared_preferences_linux","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/shared_preferences_linux-2.4.1/","native_build":false,"dependencies":["path_provider_linux"],"dev_dependency":false},{"name":"syncfusion_pdfviewer_linux","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/syncfusion_pdfviewer_linux-31.1.19/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"url_launcher_linux","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/url_launcher_linux-3.2.2/","native_build":true,"dependencies":[],"dev_dependency":false}],"windows":[{"name":"connectivity_plus","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/connectivity_plus-6.1.5/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"device_info_plus","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/device_info_plus-11.5.0/","native_build":false,"dependencies":[],"dev_dependency":false},{"name":"file_picker","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/file_picker-10.3.7/","native_build":false,"dependencies":[],"dev_dependency":false},{"name":"firebase_auth","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/firebase_auth-6.1.2/","native_build":true,"dependencies":["firebase_core"],"dev_dependency":false},{"name":"firebase_core","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/firebase_core-4.2.1/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"flutter_secure_storage_windows","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/flutter_secure_storage_windows-3.1.2/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"local_auth_windows","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/local_auth_windows-1.0.11/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"open_file_windows","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/open_file_windows-0.0.3/","native_build":false,"dependencies":[],"dev_dependency":false},{"name":"package_info_plus","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/package_info_plus-8.3.1/","native_build":false,"dependencies":[],"dev_dependency":false},{"name":"path_provider_windows","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/path_provider_windows-2.3.0/","native_build":false,"dependencies":[],"dev_dependency":false},{"name":"shared_preferences_windows","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/shared_preferences_windows-2.4.1/","native_build":false,"dependencies":["path_provider_windows"],"dev_dependency":false},{"name":"syncfusion_pdfviewer_windows","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/syncfusion_pdfviewer_windows-31.1.19/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"url_launcher_windows","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/url_launcher_windows-3.1.5/","native_build":true,"dependencies":[],"dev_dependency":false}],"web":[{"name":"connectivity_plus","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/connectivity_plus-6.1.5/","dependencies":[],"dev_dependency":false},{"name":"device_info_plus","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/device_info_plus-11.5.0/","dependencies":[],"dev_dependency":false},{"name":"file_picker","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/file_picker-10.3.7/","dependencies":[],"dev_dependency":false},{"name":"firebase_auth_web","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/firebase_auth_web-6.1.0/","dependencies":["firebase_core_web"],"dev_dependency":false},{"name":"firebase_core_web","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/firebase_core_web-3.3.0/","dependencies":[],"dev_dependency":false},{"name":"firebase_messaging_web","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/firebase_messaging_web-4.1.0/","dependencies":["firebase_core_web"],"dev_dependency":false},{"name":"flutter_secure_storage_web","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/flutter_secure_storage_web-1.2.1/","dependencies":[],"dev_dependency":false},{"name":"fluttertoast","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/fluttertoast-9.0.0/","dependencies":[],"dev_dependency":false},{"name":"google_sign_in_web","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/google_sign_in_web-1.1.0/","dependencies":[],"dev_dependency":false},{"name":"open_file_web","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/open_file_web-0.0.4/","dependencies":[],"dev_dependency":false},{"name":"package_info_plus","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/package_info_plus-8.3.1/","dependencies":[],"dev_dependency":false},{"name":"pdf_render","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/pdf_render-1.4.12/","dependencies":[],"dev_dependency":false},{"name":"shared_preferences_web","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/shared_preferences_web-2.4.3/","dependencies":[],"dev_dependency":false},{"name":"syncfusion_pdfviewer_web","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/syncfusion_pdfviewer_web-31.1.19/","dependencies":[],"dev_dependency":false},{"name":"url_launcher_web","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/url_launcher_web-2.4.1/","dependencies":[],"dev_dependency":false}]},"dependencyGraph":[{"name":"connectivity_plus","dependencies":[]},{"name":"device_info_plus","dependencies":[]},{"name":"file_picker","dependencies":["flutter_plugin_android_lifecycle"]},{"name":"firebase_auth","dependencies":["firebase_auth_web","firebase_core"]},{"name":"firebase_auth_web","dependencies":["firebase_core","firebase_core_web"]},{"name":"firebase_core","dependencies":["firebase_core_web"]},{"name":"firebase_core_web","dependencies":[]},{"name":"firebase_messaging","dependencies":["firebase_core","firebase_messaging_web"]},{"name":"firebase_messaging_web","dependencies":["firebase_core","firebase_core_web"]},{"name":"flutter_plugin_android_lifecycle","dependencies":[]},{"name":"flutter_secure_storage","dependencies":["flutter_secure_storage_linux","flutter_secure_storage_macos","flutter_secure_storage_web","flutter_secure_storage_windows"]},{"name":"flutter_secure_storage_linux","dependencies":[]},{"name":"flutter_secure_storage_macos","dependencies":[]},{"name":"flutter_secure_storage_web","dependencies":[]},{"name":"flutter_secure_storage_windows","dependencies":["path_provider"]},{"name":"fluttertoast","dependencies":[]},{"name":"google_sign_in","dependencies":["google_sign_in_android","google_sign_in_ios","google_sign_in_web"]},{"name":"google_sign_in_android","dependencies":[]},{"name":"google_sign_in_ios","dependencies":[]},{"name":"google_sign_in_web","dependencies":[]},{"name":"local_auth","dependencies":["local_auth_android","local_auth_darwin","local_auth_windows"]},{"name":"local_auth_android","dependencies":["flutter_plugin_android_lifecycle"]},{"name":"local_auth_darwin","dependencies":[]},{"name":"local_auth_windows","dependencies":[]},{"name":"open_file","dependencies":["open_file_android","open_file_web","open_file_ios","open_file_mac","open_file_windows","open_file_linux"]},{"name":"open_file_android","dependencies":[]},{"name":"open_file_ios","dependencies":[]},{"name":"open_file_linux","dependencies":[]},{"name":"open_file_mac","dependencies":[]},{"name":"open_file_web","dependencies":[]},{"name":"open_file_windows","dependencies":[]},{"name":"package_info_plus","dependencies":[]},{"name":"path_provider","dependencies":["path_provider_android","path_provider_foundation","path_provider_linux","path_provider_windows"]},{"name":"path_provider_android","dependencies":[]},{"name":"path_provider_foundation","dependencies":[]},{"name":"path_provider_linux","dependencies":[]},{"name":"path_provider_windows","dependencies":[]},{"name":"pdf_render","dependencies":[]},{"name":"shared_preferences","dependencies":["shared_preferences_android","shared_preferences_foundation","shared_preferences_linux","shared_preferences_web","shared_preferences_windows"]},{"name":"shared_preferences_android","dependencies":[]},{"name":"shared_preferences_foundation","dependencies":[]},{"name":"shared_preferences_linux","dependencies":["path_provider_linux"]},{"name":"shared_preferences_web","dependencies":[]},{"name":"shared_preferences_windows","dependencies":["path_provider_windows"]},{"name":"syncfusion_flutter_pdfviewer","dependencies":["url_launcher","device_info_plus","syncfusion_pdfviewer_web","syncfusion_pdfviewer_macos","syncfusion_pdfviewer_windows","syncfusion_pdfviewer_linux"]},{"name":"syncfusion_pdfviewer_linux","dependencies":[]},{"name":"syncfusion_pdfviewer_macos","dependencies":[]},{"name":"syncfusion_pdfviewer_web","dependencies":[]},{"name":"syncfusion_pdfviewer_windows","dependencies":[]},{"name":"url_launcher","dependencies":["url_launcher_android","url_launcher_ios","url_launcher_linux","url_launcher_macos","url_launcher_web","url_launcher_windows"]},{"name":"url_launcher_android","dependencies":[]},{"name":"url_launcher_ios","dependencies":[]},{"name":"url_launcher_linux","dependencies":[]},{"name":"url_launcher_macos","dependencies":[]},{"name":"url_launcher_web","dependencies":[]},{"name":"url_launcher_windows","dependencies":[]}],"date_created":"2026-04-03 15:06:40.682541","version":"3.35.7","swift_package_manager_enabled":{"ios":false,"macos":false}} +{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"connectivity_plus","path":"/home/venba/.pub-cache/hosted/pub.dev/connectivity_plus-6.1.5/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"device_info_plus","path":"/home/venba/.pub-cache/hosted/pub.dev/device_info_plus-11.5.0/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"file_picker","path":"/home/venba/.pub-cache/hosted/pub.dev/file_picker-10.3.7/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"firebase_auth","path":"/home/venba/.pub-cache/hosted/pub.dev/firebase_auth-6.1.2/","native_build":true,"dependencies":["firebase_core"],"dev_dependency":false},{"name":"firebase_core","path":"/home/venba/.pub-cache/hosted/pub.dev/firebase_core-4.2.1/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"firebase_messaging","path":"/home/venba/.pub-cache/hosted/pub.dev/firebase_messaging-16.0.4/","native_build":true,"dependencies":["firebase_core"],"dev_dependency":false},{"name":"flutter_secure_storage","path":"/home/venba/.pub-cache/hosted/pub.dev/flutter_secure_storage-9.2.4/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"fluttertoast","path":"/home/venba/.pub-cache/hosted/pub.dev/fluttertoast-9.0.0/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"google_sign_in_ios","path":"/home/venba/.pub-cache/hosted/pub.dev/google_sign_in_ios-6.2.1/","shared_darwin_source":true,"native_build":true,"dependencies":[],"dev_dependency":false},{"name":"local_auth_darwin","path":"/home/venba/.pub-cache/hosted/pub.dev/local_auth_darwin-1.6.0/","shared_darwin_source":true,"native_build":true,"dependencies":[],"dev_dependency":false},{"name":"open_file_ios","path":"/home/venba/.pub-cache/hosted/pub.dev/open_file_ios-1.0.3/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"package_info_plus","path":"/home/venba/.pub-cache/hosted/pub.dev/package_info_plus-8.3.1/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"path_provider_foundation","path":"/home/venba/.pub-cache/hosted/pub.dev/path_provider_foundation-2.4.2/","shared_darwin_source":true,"native_build":true,"dependencies":[],"dev_dependency":false},{"name":"pdf_render","path":"/home/venba/.pub-cache/hosted/pub.dev/pdf_render-1.4.12/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"shared_preferences_foundation","path":"/home/venba/.pub-cache/hosted/pub.dev/shared_preferences_foundation-2.5.4/","shared_darwin_source":true,"native_build":true,"dependencies":[],"dev_dependency":false},{"name":"syncfusion_flutter_pdfviewer","path":"/home/venba/.pub-cache/hosted/pub.dev/syncfusion_flutter_pdfviewer-31.1.19/","native_build":true,"dependencies":["device_info_plus"],"dev_dependency":false},{"name":"url_launcher_ios","path":"/home/venba/.pub-cache/hosted/pub.dev/url_launcher_ios-6.3.4/","native_build":true,"dependencies":[],"dev_dependency":false}],"android":[{"name":"connectivity_plus","path":"/home/venba/.pub-cache/hosted/pub.dev/connectivity_plus-6.1.5/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"device_info_plus","path":"/home/venba/.pub-cache/hosted/pub.dev/device_info_plus-11.5.0/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"file_picker","path":"/home/venba/.pub-cache/hosted/pub.dev/file_picker-10.3.7/","native_build":true,"dependencies":["flutter_plugin_android_lifecycle"],"dev_dependency":false},{"name":"firebase_auth","path":"/home/venba/.pub-cache/hosted/pub.dev/firebase_auth-6.1.2/","native_build":true,"dependencies":["firebase_core"],"dev_dependency":false},{"name":"firebase_core","path":"/home/venba/.pub-cache/hosted/pub.dev/firebase_core-4.2.1/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"firebase_messaging","path":"/home/venba/.pub-cache/hosted/pub.dev/firebase_messaging-16.0.4/","native_build":true,"dependencies":["firebase_core"],"dev_dependency":false},{"name":"flutter_plugin_android_lifecycle","path":"/home/venba/.pub-cache/hosted/pub.dev/flutter_plugin_android_lifecycle-2.0.31/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"flutter_secure_storage","path":"/home/venba/.pub-cache/hosted/pub.dev/flutter_secure_storage-9.2.4/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"fluttertoast","path":"/home/venba/.pub-cache/hosted/pub.dev/fluttertoast-9.0.0/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"google_sign_in_android","path":"/home/venba/.pub-cache/hosted/pub.dev/google_sign_in_android-7.2.1/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"local_auth_android","path":"/home/venba/.pub-cache/hosted/pub.dev/local_auth_android-1.0.52/","native_build":true,"dependencies":["flutter_plugin_android_lifecycle"],"dev_dependency":false},{"name":"open_file_android","path":"/home/venba/.pub-cache/hosted/pub.dev/open_file_android-1.0.6/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"package_info_plus","path":"/home/venba/.pub-cache/hosted/pub.dev/package_info_plus-8.3.1/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"path_provider_android","path":"/home/venba/.pub-cache/hosted/pub.dev/path_provider_android-2.2.19/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"pdf_render","path":"/home/venba/.pub-cache/hosted/pub.dev/pdf_render-1.4.12/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"shared_preferences_android","path":"/home/venba/.pub-cache/hosted/pub.dev/shared_preferences_android-2.4.13/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"syncfusion_flutter_pdfviewer","path":"/home/venba/.pub-cache/hosted/pub.dev/syncfusion_flutter_pdfviewer-31.1.19/","native_build":true,"dependencies":["device_info_plus"],"dev_dependency":false},{"name":"url_launcher_android","path":"/home/venba/.pub-cache/hosted/pub.dev/url_launcher_android-6.3.20/","native_build":true,"dependencies":[],"dev_dependency":false}],"macos":[{"name":"connectivity_plus","path":"/home/venba/.pub-cache/hosted/pub.dev/connectivity_plus-6.1.5/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"device_info_plus","path":"/home/venba/.pub-cache/hosted/pub.dev/device_info_plus-11.5.0/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"file_picker","path":"/home/venba/.pub-cache/hosted/pub.dev/file_picker-10.3.7/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"firebase_auth","path":"/home/venba/.pub-cache/hosted/pub.dev/firebase_auth-6.1.2/","native_build":true,"dependencies":["firebase_core"],"dev_dependency":false},{"name":"firebase_core","path":"/home/venba/.pub-cache/hosted/pub.dev/firebase_core-4.2.1/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"firebase_messaging","path":"/home/venba/.pub-cache/hosted/pub.dev/firebase_messaging-16.0.4/","native_build":true,"dependencies":["firebase_core"],"dev_dependency":false},{"name":"flutter_secure_storage_macos","path":"/home/venba/.pub-cache/hosted/pub.dev/flutter_secure_storage_macos-3.1.3/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"google_sign_in_ios","path":"/home/venba/.pub-cache/hosted/pub.dev/google_sign_in_ios-6.2.1/","shared_darwin_source":true,"native_build":true,"dependencies":[],"dev_dependency":false},{"name":"local_auth_darwin","path":"/home/venba/.pub-cache/hosted/pub.dev/local_auth_darwin-1.6.0/","shared_darwin_source":true,"native_build":true,"dependencies":[],"dev_dependency":false},{"name":"open_file_mac","path":"/home/venba/.pub-cache/hosted/pub.dev/open_file_mac-1.0.3/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"package_info_plus","path":"/home/venba/.pub-cache/hosted/pub.dev/package_info_plus-8.3.1/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"path_provider_foundation","path":"/home/venba/.pub-cache/hosted/pub.dev/path_provider_foundation-2.4.2/","shared_darwin_source":true,"native_build":true,"dependencies":[],"dev_dependency":false},{"name":"pdf_render","path":"/home/venba/.pub-cache/hosted/pub.dev/pdf_render-1.4.12/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"shared_preferences_foundation","path":"/home/venba/.pub-cache/hosted/pub.dev/shared_preferences_foundation-2.5.4/","shared_darwin_source":true,"native_build":true,"dependencies":[],"dev_dependency":false},{"name":"syncfusion_pdfviewer_macos","path":"/home/venba/.pub-cache/hosted/pub.dev/syncfusion_pdfviewer_macos-31.1.19/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"url_launcher_macos","path":"/home/venba/.pub-cache/hosted/pub.dev/url_launcher_macos-3.2.3/","native_build":true,"dependencies":[],"dev_dependency":false}],"linux":[{"name":"connectivity_plus","path":"/home/venba/.pub-cache/hosted/pub.dev/connectivity_plus-6.1.5/","native_build":false,"dependencies":[],"dev_dependency":false},{"name":"device_info_plus","path":"/home/venba/.pub-cache/hosted/pub.dev/device_info_plus-11.5.0/","native_build":false,"dependencies":[],"dev_dependency":false},{"name":"file_picker","path":"/home/venba/.pub-cache/hosted/pub.dev/file_picker-10.3.7/","native_build":false,"dependencies":[],"dev_dependency":false},{"name":"flutter_secure_storage_linux","path":"/home/venba/.pub-cache/hosted/pub.dev/flutter_secure_storage_linux-1.2.3/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"open_file_linux","path":"/home/venba/.pub-cache/hosted/pub.dev/open_file_linux-0.0.5/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"package_info_plus","path":"/home/venba/.pub-cache/hosted/pub.dev/package_info_plus-8.3.1/","native_build":false,"dependencies":[],"dev_dependency":false},{"name":"path_provider_linux","path":"/home/venba/.pub-cache/hosted/pub.dev/path_provider_linux-2.2.1/","native_build":false,"dependencies":[],"dev_dependency":false},{"name":"shared_preferences_linux","path":"/home/venba/.pub-cache/hosted/pub.dev/shared_preferences_linux-2.4.1/","native_build":false,"dependencies":["path_provider_linux"],"dev_dependency":false},{"name":"syncfusion_pdfviewer_linux","path":"/home/venba/.pub-cache/hosted/pub.dev/syncfusion_pdfviewer_linux-31.1.19/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"url_launcher_linux","path":"/home/venba/.pub-cache/hosted/pub.dev/url_launcher_linux-3.2.2/","native_build":true,"dependencies":[],"dev_dependency":false}],"windows":[{"name":"connectivity_plus","path":"/home/venba/.pub-cache/hosted/pub.dev/connectivity_plus-6.1.5/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"device_info_plus","path":"/home/venba/.pub-cache/hosted/pub.dev/device_info_plus-11.5.0/","native_build":false,"dependencies":[],"dev_dependency":false},{"name":"file_picker","path":"/home/venba/.pub-cache/hosted/pub.dev/file_picker-10.3.7/","native_build":false,"dependencies":[],"dev_dependency":false},{"name":"firebase_auth","path":"/home/venba/.pub-cache/hosted/pub.dev/firebase_auth-6.1.2/","native_build":true,"dependencies":["firebase_core"],"dev_dependency":false},{"name":"firebase_core","path":"/home/venba/.pub-cache/hosted/pub.dev/firebase_core-4.2.1/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"flutter_secure_storage_windows","path":"/home/venba/.pub-cache/hosted/pub.dev/flutter_secure_storage_windows-3.1.2/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"local_auth_windows","path":"/home/venba/.pub-cache/hosted/pub.dev/local_auth_windows-1.0.11/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"open_file_windows","path":"/home/venba/.pub-cache/hosted/pub.dev/open_file_windows-0.0.3/","native_build":false,"dependencies":[],"dev_dependency":false},{"name":"package_info_plus","path":"/home/venba/.pub-cache/hosted/pub.dev/package_info_plus-8.3.1/","native_build":false,"dependencies":[],"dev_dependency":false},{"name":"path_provider_windows","path":"/home/venba/.pub-cache/hosted/pub.dev/path_provider_windows-2.3.0/","native_build":false,"dependencies":[],"dev_dependency":false},{"name":"shared_preferences_windows","path":"/home/venba/.pub-cache/hosted/pub.dev/shared_preferences_windows-2.4.1/","native_build":false,"dependencies":["path_provider_windows"],"dev_dependency":false},{"name":"syncfusion_pdfviewer_windows","path":"/home/venba/.pub-cache/hosted/pub.dev/syncfusion_pdfviewer_windows-31.1.19/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"url_launcher_windows","path":"/home/venba/.pub-cache/hosted/pub.dev/url_launcher_windows-3.1.5/","native_build":true,"dependencies":[],"dev_dependency":false}],"web":[{"name":"connectivity_plus","path":"/home/venba/.pub-cache/hosted/pub.dev/connectivity_plus-6.1.5/","dependencies":[],"dev_dependency":false},{"name":"device_info_plus","path":"/home/venba/.pub-cache/hosted/pub.dev/device_info_plus-11.5.0/","dependencies":[],"dev_dependency":false},{"name":"file_picker","path":"/home/venba/.pub-cache/hosted/pub.dev/file_picker-10.3.7/","dependencies":[],"dev_dependency":false},{"name":"firebase_auth_web","path":"/home/venba/.pub-cache/hosted/pub.dev/firebase_auth_web-6.1.0/","dependencies":["firebase_core_web"],"dev_dependency":false},{"name":"firebase_core_web","path":"/home/venba/.pub-cache/hosted/pub.dev/firebase_core_web-3.3.0/","dependencies":[],"dev_dependency":false},{"name":"firebase_messaging_web","path":"/home/venba/.pub-cache/hosted/pub.dev/firebase_messaging_web-4.1.0/","dependencies":["firebase_core_web"],"dev_dependency":false},{"name":"flutter_secure_storage_web","path":"/home/venba/.pub-cache/hosted/pub.dev/flutter_secure_storage_web-1.2.1/","dependencies":[],"dev_dependency":false},{"name":"fluttertoast","path":"/home/venba/.pub-cache/hosted/pub.dev/fluttertoast-9.0.0/","dependencies":[],"dev_dependency":false},{"name":"google_sign_in_web","path":"/home/venba/.pub-cache/hosted/pub.dev/google_sign_in_web-1.1.0/","dependencies":[],"dev_dependency":false},{"name":"open_file_web","path":"/home/venba/.pub-cache/hosted/pub.dev/open_file_web-0.0.4/","dependencies":[],"dev_dependency":false},{"name":"package_info_plus","path":"/home/venba/.pub-cache/hosted/pub.dev/package_info_plus-8.3.1/","dependencies":[],"dev_dependency":false},{"name":"pdf_render","path":"/home/venba/.pub-cache/hosted/pub.dev/pdf_render-1.4.12/","dependencies":[],"dev_dependency":false},{"name":"shared_preferences_web","path":"/home/venba/.pub-cache/hosted/pub.dev/shared_preferences_web-2.4.3/","dependencies":[],"dev_dependency":false},{"name":"syncfusion_pdfviewer_web","path":"/home/venba/.pub-cache/hosted/pub.dev/syncfusion_pdfviewer_web-31.1.19/","dependencies":[],"dev_dependency":false},{"name":"url_launcher_web","path":"/home/venba/.pub-cache/hosted/pub.dev/url_launcher_web-2.4.1/","dependencies":[],"dev_dependency":false}]},"dependencyGraph":[{"name":"connectivity_plus","dependencies":[]},{"name":"device_info_plus","dependencies":[]},{"name":"file_picker","dependencies":["flutter_plugin_android_lifecycle"]},{"name":"firebase_auth","dependencies":["firebase_auth_web","firebase_core"]},{"name":"firebase_auth_web","dependencies":["firebase_core","firebase_core_web"]},{"name":"firebase_core","dependencies":["firebase_core_web"]},{"name":"firebase_core_web","dependencies":[]},{"name":"firebase_messaging","dependencies":["firebase_core","firebase_messaging_web"]},{"name":"firebase_messaging_web","dependencies":["firebase_core","firebase_core_web"]},{"name":"flutter_plugin_android_lifecycle","dependencies":[]},{"name":"flutter_secure_storage","dependencies":["flutter_secure_storage_linux","flutter_secure_storage_macos","flutter_secure_storage_web","flutter_secure_storage_windows"]},{"name":"flutter_secure_storage_linux","dependencies":[]},{"name":"flutter_secure_storage_macos","dependencies":[]},{"name":"flutter_secure_storage_web","dependencies":[]},{"name":"flutter_secure_storage_windows","dependencies":["path_provider"]},{"name":"fluttertoast","dependencies":[]},{"name":"google_sign_in","dependencies":["google_sign_in_android","google_sign_in_ios","google_sign_in_web"]},{"name":"google_sign_in_android","dependencies":[]},{"name":"google_sign_in_ios","dependencies":[]},{"name":"google_sign_in_web","dependencies":[]},{"name":"local_auth","dependencies":["local_auth_android","local_auth_darwin","local_auth_windows"]},{"name":"local_auth_android","dependencies":["flutter_plugin_android_lifecycle"]},{"name":"local_auth_darwin","dependencies":[]},{"name":"local_auth_windows","dependencies":[]},{"name":"open_file","dependencies":["open_file_android","open_file_web","open_file_ios","open_file_mac","open_file_windows","open_file_linux"]},{"name":"open_file_android","dependencies":[]},{"name":"open_file_ios","dependencies":[]},{"name":"open_file_linux","dependencies":[]},{"name":"open_file_mac","dependencies":[]},{"name":"open_file_web","dependencies":[]},{"name":"open_file_windows","dependencies":[]},{"name":"package_info_plus","dependencies":[]},{"name":"path_provider","dependencies":["path_provider_android","path_provider_foundation","path_provider_linux","path_provider_windows"]},{"name":"path_provider_android","dependencies":[]},{"name":"path_provider_foundation","dependencies":[]},{"name":"path_provider_linux","dependencies":[]},{"name":"path_provider_windows","dependencies":[]},{"name":"pdf_render","dependencies":[]},{"name":"shared_preferences","dependencies":["shared_preferences_android","shared_preferences_foundation","shared_preferences_linux","shared_preferences_web","shared_preferences_windows"]},{"name":"shared_preferences_android","dependencies":[]},{"name":"shared_preferences_foundation","dependencies":[]},{"name":"shared_preferences_linux","dependencies":["path_provider_linux"]},{"name":"shared_preferences_web","dependencies":[]},{"name":"shared_preferences_windows","dependencies":["path_provider_windows"]},{"name":"syncfusion_flutter_pdfviewer","dependencies":["url_launcher","device_info_plus","syncfusion_pdfviewer_web","syncfusion_pdfviewer_macos","syncfusion_pdfviewer_windows","syncfusion_pdfviewer_linux"]},{"name":"syncfusion_pdfviewer_linux","dependencies":[]},{"name":"syncfusion_pdfviewer_macos","dependencies":[]},{"name":"syncfusion_pdfviewer_web","dependencies":[]},{"name":"syncfusion_pdfviewer_windows","dependencies":[]},{"name":"url_launcher","dependencies":["url_launcher_android","url_launcher_ios","url_launcher_linux","url_launcher_macos","url_launcher_web","url_launcher_windows"]},{"name":"url_launcher_android","dependencies":[]},{"name":"url_launcher_ios","dependencies":[]},{"name":"url_launcher_linux","dependencies":[]},{"name":"url_launcher_macos","dependencies":[]},{"name":"url_launcher_web","dependencies":[]},{"name":"url_launcher_windows","dependencies":[]}],"date_created":"2026-04-04 10:24:29.151927","version":"3.41.4","swift_package_manager_enabled":{"ios":false,"macos":false}} \ No newline at end of file diff --git a/lib/core/routing/app_router.dart b/lib/core/routing/app_router.dart index 59e4376..11064d7 100644 --- a/lib/core/routing/app_router.dart +++ b/lib/core/routing/app_router.dart @@ -21,7 +21,7 @@ import '../../presentation/screens/Masters/EndorsementType/endorsementList.dart' import '../../presentation/screens/Masters/VehicleType/vehicleList.dart'; import '../../presentation/screens/StaffAttendance/attendanceAllDetails.dart'; import '../../presentation/screens/StaffAttendance/individual_Attendance.dart'; -import '../../presentation/screens/UserManagement/Agent/agent.dart'; +import '../../presentation/screens/UserManagement/Agent/agent_details.dart'; import '../../presentation/screens/UserManagement/Agent/agentList.dart'; import '../../presentation/screens/UserManagement/POS/pos_list.dart'; import '../../presentation/screens/UserManagement/Profile/profile_mobile.dart'; @@ -134,6 +134,14 @@ final GoRouter appRouter = GoRouter( builder: (context, state) => const AgentList(), ), + GoRoute( + path: AppRoutes.agentDetailsParam, + builder: (context, state) { + final agentId = state.pathParameters['agentId'] ?? 'new'; + return AgentDetailsScreen(agentId: agentId); + }, + ), + // GoRoute( // path: '/agent/:id', // builder: (context, state) { diff --git a/lib/core/routing/routes.dart b/lib/core/routing/routes.dart index 2677286..ddc8753 100644 --- a/lib/core/routing/routes.dart +++ b/lib/core/routing/routes.dart @@ -22,6 +22,11 @@ class AppRoutes { static const String profile = '/profile'; static const String agentLst = '/agentLst'; static const agent = '/agent/:id'; + /// Partner details page: use [agentDetailsFor] with `'new'` or agent id. + static const String agentDetailsParam = '/agentDetails/:agentId'; + + static String agentDetailsFor(String agentId) => + '/agentDetails/${Uri.encodeComponent(agentId)}'; static const String staffLst = '/staffLst'; static const staff = '/staff/:id'; static const String salesExecutiveLst = '/salesExecutiveLst'; diff --git a/lib/core/services/api_service.dart b/lib/core/services/api_service.dart index e10e400..a95e8ee 100644 --- a/lib/core/services/api_service.dart +++ b/lib/core/services/api_service.dart @@ -915,13 +915,18 @@ class ApiService { } // ----------------------------------- PAYOUT GRID ---------------------------------------------- - Future> loadPayoutGrid({String? role, String? fileId}) async { + Future> loadPayoutGrid({ + String? role, + String? fileId, + String? loggedId, + }) async { if (_token == null) { await _initializeToken(); } final roleTrimmed = role?.trim(); final fileIdTrimmed = fileId?.trim(); + final loggedIdTrimmed = loggedId?.trim(); final endpoint = Uri.parse('${Env.apiUrl}grid'); final queryParameters = {}; @@ -931,6 +936,9 @@ class ApiService { if (fileIdTrimmed != null && fileIdTrimmed.isNotEmpty) { queryParameters['file_id'] = fileIdTrimmed; } + if (loggedIdTrimmed != null && loggedIdTrimmed.isNotEmpty) { + queryParameters['logged_id'] = loggedIdTrimmed; + } final url = endpoint.replace(queryParameters: queryParameters); final headers = { @@ -1059,6 +1067,65 @@ class ApiService { return response; } + Future> fetchPartnerVehicleTypes() async { + if (_token == null) { + await _initializeToken(); + } + final url = Uri.parse('${Env.apiUrl}agent/partnerVehicleTypeList'); + final headers = { + 'Authorization': 'Bearer $_token' ?? '', + 'app-signature': Env.App_Signature, + }; + return _makeGetRequest(url, headers); + } + + Future> updateAgentVehicleRetention({ + required String agentId, + required String vehicleTypeId, + required String retentionRate, + required String updatedBy, + }) async { + if (_token == null) { + await _initializeToken(); + } + final url = Uri.parse('${Env.apiUrl}agent/updateAgentVehicleRetention'); + final headers = { + 'Authorization': 'Bearer $_token' ?? '', + 'Content-Type': 'application/json', + 'app-signature': Env.App_Signature, + }; + final body = { + 'agent_id': agentId, + 'vehicle_type_id': vehicleTypeId, + 'retention_rate': retentionRate, + 'updated_by': updatedBy, + }; + return _makePostRequestJson(url, body, headers); + } + + /// Bulk upsert retention rows for an agent (Save all in retention table). + Future> saveAgentRetentionRatesBulk({ + required String agentId, + required List> retentionRates, + required String updatedBy, + }) async { + if (_token == null) { + await _initializeToken(); + } + final url = Uri.parse('${Env.apiUrl}agent/saveAgentRetentionRatesBulk'); + final headers = { + 'Authorization': 'Bearer $_token' ?? '', + 'Content-Type': 'application/json', + 'app-signature': Env.App_Signature, + }; + final body = { + 'agent_id': agentId, + 'updated_by': updatedBy, + 'retention_rates': retentionRates, + }; + return _makePostRequestJson(url, body, headers); + } + Future> deleteAgentIncentiveFile(id) async { // print(_token); if (_token == null) { diff --git a/lib/data/utils/validators.dart b/lib/data/utils/validators.dart index e675de6..b3b917b 100644 --- a/lib/data/utils/validators.dart +++ b/lib/data/utils/validators.dart @@ -34,10 +34,12 @@ class Validators { return "Required"; } - final emailRegex = r'^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$'; + final trimmed = value.trim(); + final emailRegex = + r'^[a-zA-Z0-9](?:[a-zA-Z0-9._%+-]*[a-zA-Z0-9])?@(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}$'; - if (!RegExp(emailRegex).hasMatch(value)) { - return "Invalid $label"; // 👈 will show: "Invalid Email" + if (!RegExp(emailRegex).hasMatch(trimmed)) { + return "Invalid $label"; } return null; } diff --git a/lib/presentation/screens/Grid/gridView.dart b/lib/presentation/screens/Grid/gridView.dart index 10c2320..8a4ee10 100644 --- a/lib/presentation/screens/Grid/gridView.dart +++ b/lib/presentation/screens/Grid/gridView.dart @@ -83,6 +83,10 @@ class _gridViewScreenState extends ConsumerState { bool _isUpdating = false; String _retentionRate = '-'; + /// Normalized vehicle type key → retention % from [partner_retention_rate] (via findAgent `retention_by_vehicle`). + /// Also keys `id:` when the API returns `vehicle_type_id`. + final Map _retentionByVehicleKey = {}; + @override void dispose() { _searchController.dispose(); @@ -107,8 +111,16 @@ class _gridViewScreenState extends ConsumerState { @override void initState() { super.initState(); - _loadRetentionRateFromToken(); - _fetch(); + WidgetsBinding.instance.addPostFrameCallback((_) => _bootstrapGrid()); + } + + Future _bootstrapGrid() async { + await Future.wait([ + _loadRetentionRateFromToken(), + _loadPartnerRetentionByVehicle(), + ]); + if (!mounted) return; + await _fetch(); } Future _loadRetentionRateFromToken() async { @@ -122,14 +134,17 @@ class _gridViewScreenState extends ConsumerState { final retentionRate = data['retention_rate']?.toString(); final loggedId = data['id']?.toString(); - if (retentionRate == null || retentionRate.trim().isEmpty) return; if (mounted) { setState(() { _loggedId = (loggedId == null || loggedId.trim().isEmpty) ? null : loggedId.trim(); - _retentionRate = retentionRate.trim(); + if (retentionRate != null && retentionRate.trim().isNotEmpty) { + _retentionRate = retentionRate.trim(); + } else { + _retentionRate = '-'; + } _grid = _applyRetentionToGridRows(_grid); _filteredGrid = _applyRetentionToGridRows(_filteredGrid); }); @@ -139,6 +154,88 @@ class _gridViewScreenState extends ConsumerState { } } + String _normalizeVehicleTypeKey(String raw) { + return raw.toLowerCase().trim().replaceAll(RegExp(r'\s+'), ' '); + } + + /// Loads per–vehicle-type retention for the logged-in agent, or for Accounts with `?agent_id=` on grid URL. + Future _loadPartnerRetentionByVehicle() async { + final appRole = + (ref.read(userRoleProvider) ?? '').toString().trim().toLowerCase(); + String? agentPk; + if (appRole == 'agent') { + try { + final token = await AuthService.getToken(); + if (token == null || token.isEmpty) return; + final data = Jwt.parseJwt(token)['data']; + if (data is Map) { + agentPk = (data['id']?.toString() ?? '').trim(); + if (agentPk.isEmpty) agentPk = null; + } + } catch (_) { + return; + } + } else if (appRole == 'accounts') { + agentPk = Uri.base.queryParameters['agent_id']?.trim(); + if (agentPk != null && agentPk.isEmpty) agentPk = null; + } + + if (agentPk == null || agentPk.isEmpty) { + if (mounted) { + setState(() { + _retentionByVehicleKey.clear(); + _grid = _applyRetentionToGridRows(_grid); + _filteredGrid = _applyRetentionToGridRows(_filteredGrid); + }); + } + return; + } + + try { + final res = await _apiService.findSingleAgentData(agentPk); + if ((res['status'] ?? '').toString().toLowerCase() != 'success' || + res['data'] == null) { + return; + } + final data = Map.from(res['data'] as Map); + final raw = data['retention_by_vehicle'] ?? data['retentionByVehicle']; + final next = {}; + if (raw is List) { + for (final item in raw) { + if (item is! Map) continue; + final m = Map.from(item); + final rate = _toNum(m['retention_rate'] ?? m['retentionRate']); + if (rate == null) continue; + final name = (m['vehicle_type'] ?? '').toString().trim(); + if (name.isNotEmpty) { + next[_normalizeVehicleTypeKey(name)] = rate; + } + final vid = + int.tryParse((m['vehicle_type_id'] ?? m['vehicleTypeId'] ?? '') + .toString()); + if (vid != null) { + next['id:$vid'] = rate; + } + } + } + if (mounted) { + setState(() { + _retentionByVehicleKey + ..clear() + ..addAll(next); + _grid = _applyRetentionToGridRows(_grid); + _filteredGrid = _applyRetentionToGridRows(_filteredGrid); + }); + } + } catch (_) { + if (mounted) { + setState(() { + _retentionByVehicleKey.clear(); + }); + } + } + } + String? _apiRoleFromAppRole(String? role) { if (role == null) return null; final r = role.trim(); @@ -194,9 +291,13 @@ class _gridViewScreenState extends ConsumerState { } } + final loggedIdForGrid = + (effectiveRole ?? '').toLowerCase() == 'agent' ? _loggedId : null; + final res = await _apiService.loadPayoutGrid( role: effectiveRole, fileId: effectiveFileId, + loggedId: loggedIdForGrid, ); if ((res['status'] ?? '').toString().toLowerCase() != 'success') { ToastHelper.showErrorToast( @@ -352,7 +453,7 @@ class _gridViewScreenState extends ConsumerState { setState(() { _filteredGrid = next; - if (_showAddButton && !_hasAnyFilterSelection) { + if (_showPartnerRetentionValues && !_hasAnyFilterSelection) { _filteredGrid = []; } _currentPage = 1; @@ -398,33 +499,73 @@ class _gridViewScreenState extends ConsumerState { return num.tryParse(normalized); } - String _partnerValueFrom(dynamic baseValue) { + num? _retentionPercentForGridRowPreview(Map row) { + final vt = row['vehicle_type']?.toString().trim() ?? ''; + if (vt.isNotEmpty) { + final byName = _retentionByVehicleKey[_normalizeVehicleTypeKey(vt)]; + if (byName != null) return byName; + } + final vid = + int.tryParse(row['vehicle_type_id']?.toString() ?? ''); + if (vid != null) { + final byId = _retentionByVehicleKey['id:$vid']; + if (byId != null) return byId; + } + return null; + } + + /// Accounts preview: subtract loaded per–vehicle retention; ≤0 → '-'; no match → show base. + String _partnerValueForRowPreview(Map row, dynamic baseValue) { final base = _toNum(baseValue); - final retention = _toNum(_retentionRate); - if (base == null || retention == null) return '-'; + if (base == null) return '-'; + final retention = _retentionPercentForGridRowPreview(row); + if (retention == null) { + if (base == base.roundToDouble()) return base.toInt().toString(); + return base + .toStringAsFixed(2) + .replaceFirst(RegExp(r'0+$'), '') + .replaceFirst(RegExp(r'\.$'), ''); + } final result = base - retention; + if (result <= 0) return '-'; if (result == result.roundToDouble()) { return result.toInt().toString(); } - return result.toStringAsFixed(2).replaceFirst(RegExp(r'0+$'), '').replaceFirst(RegExp(r'\.$'), ''); + return result + .toStringAsFixed(2) + .replaceFirst(RegExp(r'0+$'), '') + .replaceFirst(RegExp(r'\.$'), ''); } List> _applyRetentionToGridRows( List> rows, ) { - if (!_showAddButton) { + if (!_showPartnerRetentionValues) { return rows - .map((row) => Map.from(row) - ..remove('partner_comp') - ..remove('partner_tp') - ..remove('partner_od')) + .map( + (row) => Map.from(row) + ..remove('partner_comp') + ..remove('partner_tp') + ..remove('partner_od'), + ) .toList(); } + // Partner (agent): API already applies retention on comp/tp/od when logged_id is sent. + if (_showAddButton) { + return rows.map((row) { + final next = Map.from(row); + next['partner_comp'] = next['comp']; + next['partner_tp'] = next['tp']; + next['partner_od'] = next['od']; + return next; + }).toList(); + } + // Accounts + ?agent_id= preview: client-side using findAgent retention map. return rows.map((row) { final next = Map.from(row); - next['partner_comp'] = _partnerValueFrom(next['comp']); - next['partner_tp'] = _partnerValueFrom(next['tp']); - next['partner_od'] = _partnerValueFrom(next['od']); + next['partner_comp'] = _partnerValueForRowPreview(next, next['comp']); + next['partner_tp'] = _partnerValueForRowPreview(next, next['tp']); + next['partner_od'] = _partnerValueForRowPreview(next, next['od']); return next; }).toList(); } @@ -440,7 +581,16 @@ class _gridViewScreenState extends ConsumerState { return role == 'agent'; } - bool get _showPartnerRetentionValues => _showAddButton; + /// Partner (agent) login, or Accounts opening grid with `?agent_id=` to preview that partner’s payouts. + bool get _accountsAgentPreview { + final role = (ref.watch(userRoleProvider) ?? '').toString().trim().toLowerCase(); + if (role != 'accounts') return false; + final aid = Uri.base.queryParameters['agent_id']?.trim(); + return aid != null && aid.isNotEmpty; + } + + bool get _showPartnerRetentionValues => + _showAddButton || _accountsAgentPreview; bool get _showCompColumnForAgent => !_showAddButton || _selectedPlanTypeValue == 'comp'; @@ -807,7 +957,7 @@ class _gridViewScreenState extends ConsumerState { icon: Icons.search, message: 'Search', onTap: () { - if (_showAddButton && !_hasAnyFilterSelection) { + if (_showPartnerRetentionValues && !_hasAnyFilterSelection) { ToastHelper.showInfoToast( context, 'Please select at least one filter to view/export data.', @@ -975,7 +1125,7 @@ class _gridViewScreenState extends ConsumerState { showCompColumn: _showCompColumnForAgent, showTpColumn: _showTpColumnForAgent, showOdColumn: _showOdColumnForAgent, - showPartnerCompValue: _showAddButton, + showPartnerCompValue: _showPartnerRetentionValues, ), ); } @@ -1139,7 +1289,7 @@ class _gridViewScreenState extends ConsumerState { onTap: _isLoading ? () {} : () async { - if (_showAddButton && !_hasAnyFilterSelection) { + if (_showPartnerRetentionValues && !_hasAnyFilterSelection) { if (mounted) { ToastHelper.showInfoToast( context, @@ -1157,6 +1307,8 @@ class _gridViewScreenState extends ConsumerState { (roleLc == 'manager' || roleLc == 'accounts') ? fileIdFromQuery : null; + final previewAgentId = + queryParams['agent_id']?.trim(); await _apiService.downloadGridExcel( role: role, fileId: fileIdToSend, @@ -1166,7 +1318,9 @@ class _gridViewScreenState extends ConsumerState { rto: _selectedRto, planType: _selectedPlanTypeValue, search: _searchController.text.trim(), - loggedId: _loggedId, + loggedId: previewAgentId != null && previewAgentId.isNotEmpty + ? previewAgentId + : _loggedId, ); } catch (e) { if (mounted) { diff --git a/lib/presentation/screens/UserManagement/Agent/agentList.dart b/lib/presentation/screens/UserManagement/Agent/agentList.dart index 569de5c..4862e0d 100644 --- a/lib/presentation/screens/UserManagement/Agent/agentList.dart +++ b/lib/presentation/screens/UserManagement/Agent/agentList.dart @@ -15,7 +15,6 @@ import '../../../themes/indicators/filter_btn.dart'; import '../../../themes/indicators/search_field_theme.dart'; import '../../../themes/indicators/text_field_theme.dart'; import '../../../widgets/custom_action_popup.dart'; -import 'agent.dart'; import 'agentIncentiveFile.dart'; class AgentList extends ConsumerStatefulWidget { @@ -163,7 +162,7 @@ class AgentListState extends ConsumerState { Navigator.pop(context); print('EDIT - ${data['id']}'); dynamic id = data['id']; - context.go('/agent/$id'); + context.go(AppRoutes.agentDetailsFor(id.toString())); }, child: Row( mainAxisSize: MainAxisSize.min, @@ -177,20 +176,6 @@ class AgentListState extends ConsumerState { ]; } - Future showAgent({required String id}) { - print('showAgent : $id'); - return showDialog( - context: context, - builder: (ctx) => Agent( - id: id, - onSubmit: (value) { - debugPrint("New Claims: $value"); - refresh(); - }, - ), - ); - } - @override Widget build(BuildContext context) { managerId = ref.watch(managerIdProvider); @@ -352,8 +337,7 @@ class AgentListState extends ConsumerState { SizedBox(width: 10), InkWell( onTap: () { - // context.go('/agent/create'); - showAgent(id: 'Create'); + context.go(AppRoutes.agentDetailsFor('new')); }, child: Container( padding: EdgeInsets.all(5.0), @@ -620,8 +604,7 @@ class AgentListState extends ConsumerState { print('EDITStaff - ${item['id']}'); selected_id = item['id']; - showAgent(id: selected_id); - // context.go('/agent/$selected_id'); + context.go(AppRoutes.agentDetailsFor(selected_id.toString())); }, splashRadius: 28, hoverColor: Colors.black12, diff --git a/lib/presentation/screens/UserManagement/Agent/agent_details.dart b/lib/presentation/screens/UserManagement/Agent/agent_details.dart new file mode 100644 index 0000000..89ba391 --- /dev/null +++ b/lib/presentation/screens/UserManagement/Agent/agent_details.dart @@ -0,0 +1,1261 @@ +import 'dart:convert'; +import 'dart:math' as math; + +import 'package:dropdown_search/dropdown_search.dart'; +import 'package:file_picker/file_picker.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:go_router/go_router.dart'; +import 'package:google_fonts/google_fonts.dart'; +import 'package:http/http.dart' as http; + +import '../../../../core/config/env.dart'; +import '../../../../core/routing/routes.dart'; +import '../../../../core/services/api_service.dart'; +import '../../../../data/services/auth_service.dart'; +import '../../../../data/utils/toastNotification.dart'; +import '../../../../data/utils/validators.dart'; +import '../../../layouts/main_layout.dart'; +import '../../../layouts/responsive_layout.dart'; +import '../../../providers/manager_provider.dart'; +import '../../../providers/user_provider.dart'; +import '../../../providers/userRoleProvider.dart'; +import '../../../themes/indicators/input_field_decoration.dart'; +import '../../../themes/indicators/search_field_theme.dart'; +import '../../../themes/indicators/text_field_theme.dart'; +import '../../../themes/indicators/customizd_file_upload.dart'; + +/// Full-page partner (agent) create / edit with per–vehicle-type retention rates. +class AgentDetailsScreen extends ConsumerStatefulWidget { + const AgentDetailsScreen({super.key, required this.agentId}); + + /// Use `'new'` for create, otherwise the numeric agent id as string. + final String agentId; + + bool get isCreate { + final s = agentId.trim().toLowerCase(); + return s == 'new' || s == 'create'; + } + + @override + ConsumerState createState() => _AgentDetailsScreenState(); +} + +class _AgentDetailsScreenState extends ConsumerState { + /// Digits only, max 10 (India-style mobile). + static final List _phoneInputFormatters = [ + FilteringTextInputFormatter.digitsOnly, + LengthLimitingTextInputFormatter(10), + ]; + + /// Percentage 0–100 with up to 2 decimal places; cannot exceed 100. + static final List _retentionPercentFormatters = [ + FilteringTextInputFormatter.allow(RegExp(r'^\d{0,3}(\.\d{0,2})?$')), + TextInputFormatter.withFunction((oldValue, newValue) { + if (newValue.text.isEmpty) return newValue; + final v = double.tryParse(newValue.text); + if (v == null) return oldValue; + if (v > 100 || v < 0) return oldValue; + return newValue; + }), + ]; + + final _formKey = GlobalKey(); + final ApiService _api = ApiService(); + + final _nameCtrl = TextEditingController(); + final _emailCtrl = TextEditingController(); + final _mobileCtrl = TextEditingController(); + final _codeCtrl = TextEditingController(); + final _addressCtrl = TextEditingController(); + final _retentionSearchCtrl = TextEditingController(); + + final GlobalKey>> _seKey = + GlobalKey>>(); + + List> _vehicleTypes = []; + final Map _rateControllers = {}; + + List> _salesExecutives = []; + dynamic _selectedSalesExecutiveId; + + PlatformFile? _certificateFile; + String? _certificateLabel; + String? _certificateUrlFromApi; + String? _token; + String? _loadedAgentPk; + + bool _loading = true; + bool _saving = false; + bool _savingAllRetention = false; + + /// Retention rows can be saved only after the partner exists (`agent_id`). + bool get _retentionTableEnabled => + _loadedAgentPk != null && _loadedAgentPk!.trim().isNotEmpty; + + bool get _retentionActionsBusy => _saving || _savingAllRetention; + + @override + void initState() { + super.initState(); + _retentionSearchCtrl.addListener(() => setState(() {})); + _bootstrap(); + } + + Future _bootstrap() async { + _token = await AuthService.getToken(); + await _loadSalesExecutives(); + await _loadVehicleTypes(); + if (!widget.isCreate) { + await _loadAgent(); + } else { + _initRateControllersFromTypes(); + } + if (mounted) setState(() => _loading = false); + } + + Future _loadSalesExecutives() async { + final managerId = ref.read(managerIdProvider); + final role = ref.read(userRoleProvider); + if (managerId == null || role == null) return; + try { + final res = await _api.fetchSalesExecutiveList(managerId, role); + if (res['status'] == 'success' && res['data'] is List) { + final list = List>.from(res['data']); + setState(() { + _salesExecutives = + list.where((e) => int.tryParse(e['is_active'].toString()) == 1).toList(); + }); + } + } catch (_) {} + } + + Future _loadVehicleTypes() async { + try { + final res = await _api.fetchPartnerVehicleTypes(); + if (res['status'] == 'success' && res['data'] is List) { + setState(() { + _vehicleTypes = List>.from(res['data']); + }); + } + } catch (_) {} + } + + void _initRateControllersFromTypes({Map? initial}) { + for (final c in _rateControllers.values) { + c.dispose(); + } + _rateControllers.clear(); + for (final vt in _vehicleTypes) { + final id = int.tryParse(vt['id'].toString()) ?? 0; + if (id == 0) continue; + final text = initial?[id] ?? ''; + _rateControllers[id] = TextEditingController(text: text); + } + } + + /// Builds vehicle_type_id → retention % text from [findAgent] (or compatible) payload. + Map _retentionMapFromAgentData(Map data) { + final Map initial = {}; + final raw = data['retention_by_vehicle'] ?? data['retentionByVehicle']; + if (raw is! List) return initial; + for (final row in raw) { + if (row is! Map) continue; + final m = Map.from(row); + final vid = int.tryParse( + (m['vehicle_type_id'] ?? m['vehicleTypeId'] ?? '').toString(), + ) ?? + 0; + if (vid == 0) continue; + final rate = m['retention_rate'] ?? m['retentionRate']; + if (rate == null) continue; + initial[vid] = rate.toString(); + } + return initial; + } + + Future _loadAgent({bool silent = false}) async { + try { + final res = await _api.findSingleAgentData(widget.agentId); + if (res['status'] != 'success' || res['data'] == null) { + if (!silent && mounted) { + ToastHelper.showErrorToast(context, 'Partner not found'); + context.go(AppRoutes.agentLst); + } + return; + } + final data = Map.from(res['data'] as Map); + _loadedAgentPk = data['id']?.toString(); + + _nameCtrl.text = (data['name'] ?? '').toString(); + _emailCtrl.text = (data['email'] ?? '').toString(); + _mobileCtrl.text = (data['mobile'] ?? '').toString(); + _codeCtrl.text = (data['agent_code'] ?? '').toString(); + _addressCtrl.text = (data['address'] ?? '').toString(); + + if (data['sales_executive_id'] != null) { + _selectedSalesExecutiveId = int.tryParse(data['sales_executive_id'].toString()); + } + + final cert = data['certificate_file_name']?.toString(); + if (cert != null && cert.isNotEmpty) { + _certificateLabel = cert.split('/').last; + _certificateUrlFromApi = cert; + _certificateFile = null; + } + + final initial = _retentionMapFromAgentData(data); + _initRateControllersFromTypes(initial: initial); + if (silent && mounted) setState(() {}); + } catch (e) { + if (!silent && mounted) { + ToastHelper.showErrorToast(context, 'Failed to load partner'); + context.go(AppRoutes.agentLst); + } + } + } + + List> get _filteredVehicleTypes { + final q = _retentionSearchCtrl.text.trim().toLowerCase(); + if (q.isEmpty) return _vehicleTypes; + return _vehicleTypes + .where((vt) => (vt['vehicle_type'] ?? '').toString().toLowerCase().contains(q)) + .toList(); + } + + @override + void dispose() { + _nameCtrl.dispose(); + _emailCtrl.dispose(); + _mobileCtrl.dispose(); + _codeCtrl.dispose(); + _addressCtrl.dispose(); + _retentionSearchCtrl.dispose(); + for (final c in _rateControllers.values) { + c.dispose(); + } + super.dispose(); + } + + /// Collect valid rates from **all** vehicle types (not search-filtered) for bulk API. + List> _collectBulkRetentionPayload() { + final out = >[]; + for (final vt in _vehicleTypes) { + final id = int.tryParse(vt['id'].toString()) ?? 0; + if (id == 0) continue; + final ctrl = _rateControllers[id]; + if (ctrl == null) continue; + final t = ctrl.text.trim(); + if (t.isEmpty) continue; + final v = double.tryParse(t); + if (v == null || v < 0 || v > 100) continue; + out.add({ + 'vehicle_type_id': id, + 'retention_rate': v, + }); + } + return out; + } + + Future _saveAllRetentionRates() async { + if (!_retentionTableEnabled) { + ToastHelper.showWarningToast( + context, + 'Save partner details first to get an agent id', + ); + return; + } + if (_retentionActionsBusy) return; + + final rates = _collectBulkRetentionPayload(); + if (rates.isEmpty) { + ToastHelper.showWarningToast( + context, + 'Enter at least one retention rate (0–100%)', + ); + return; + } + + final userId = ref.read(userIdProvider)?.toString() ?? ''; + if (userId.isEmpty) { + ToastHelper.showErrorToast(context, 'Please log in again'); + return; + } + + if (_token == null) { + _token = await AuthService.getToken(); + } + if (_token == null) { + ToastHelper.showErrorToast(context, 'Please log in again'); + return; + } + + setState(() => _savingAllRetention = true); + try { + final res = await _api.saveAgentRetentionRatesBulk( + agentId: _loadedAgentPk!, + retentionRates: rates, + updatedBy: userId, + ); + if (res['status'] == 'success') { + if (mounted) { + ToastHelper.showSuccessToast(context, 'All retention rates saved'); + context.go(AppRoutes.agentLst); + } + return; + } else { + ToastHelper.showErrorToast( + context, + res['data']?.toString() ?? 'Save failed', + ); + } + } catch (e) { + ToastHelper.showErrorToast(context, 'Save failed'); + } finally { + if (mounted) setState(() => _savingAllRetention = false); + } + } + + Future _saveRowRetention(int vehicleTypeId) async { + if (!_retentionTableEnabled) { + ToastHelper.showWarningToast( + context, + 'Save partner details first to get an agent id', + ); + return; + } + if (_savingAllRetention) return; + final ctrl = _rateControllers[vehicleTypeId]; + if (ctrl == null) return; + final t = ctrl.text.trim(); + if (t.isEmpty) { + ToastHelper.showWarningToast(context, 'Enter a retention rate'); + return; + } + final v = double.tryParse(t); + if (v == null || v < 0 || v > 100) { + ToastHelper.showWarningToast(context, 'Rate must be between 0 and 100'); + return; + } + final userId = ref.read(userIdProvider)?.toString() ?? ''; + if (userId.isEmpty) { + ToastHelper.showErrorToast(context, 'Please log in again'); + return; + } + setState(() => _saving = true); + try { + final res = await _api.updateAgentVehicleRetention( + agentId: _loadedAgentPk!, + vehicleTypeId: '$vehicleTypeId', + retentionRate: t, + updatedBy: userId, + ); + if (res['status'] == 'success') { + if (mounted) { + ToastHelper.showSuccessToast(context, 'Retention updated'); + await _loadAgent(silent: true); + } + } else { + ToastHelper.showErrorToast( + context, + res['data']?.toString() ?? 'Update failed', + ); + } + } catch (e) { + ToastHelper.showErrorToast(context, 'Update failed'); + } finally { + if (mounted) setState(() => _saving = false); + } + } + + Future _savePartnerDetails() async { + final ok = _formKey.currentState?.validate() ?? false; + if (!ok) { + FocusManager.instance.primaryFocus?.unfocus(); + WidgetsBinding.instance.addPostFrameCallback((_) { + FocusManager.instance.primaryFocus?.unfocus(); + }); + return; + } + + final email = _emailCtrl.text.trim(); + final phone = _mobileCtrl.text.trim(); + if (_selectedSalesExecutiveId == null) { + ToastHelper.showWarningToast(context, 'Select a sales executive'); + return; + } + + if (_token == null) { + _token = await AuthService.getToken(); + } + if (_token == null) { + ToastHelper.showErrorToast(context, 'Please log in again'); + return; + } + + final managerId = ref.read(managerIdProvider)?.toString() ?? ''; + final userId = ref.read(userIdProvider)?.toString() ?? ''; + final isUpdate = !widget.isCreate && _loadedAgentPk != null; + + final uri = Uri.parse( + isUpdate ? '${Env.apiUrl}agent/updateAgent' : '${Env.apiUrl}agent/createAgent', + ); + final request = http.MultipartRequest('POST', uri); + request.headers['Authorization'] = 'Bearer $_token'; + request.headers['app-signature'] = Env.App_Signature; + + request.fields['name'] = _nameCtrl.text.trim(); + request.fields['email'] = email; + request.fields['mobile'] = phone; + request.fields['address'] = _addressCtrl.text.trim(); + request.fields['agent_code'] = _codeCtrl.text.trim(); + request.fields['sales_executive_id'] = _selectedSalesExecutiveId.toString(); + request.fields['manager_id'] = managerId; + + if (isUpdate) { + request.fields['id'] = _loadedAgentPk!; + request.fields['updated_by'] = userId; + } else { + request.fields['created_by'] = userId; + } + + if (_certificateFile != null) { + try { + if (_certificateFile!.bytes != null) { + request.files.add( + http.MultipartFile.fromBytes( + 'certificate_file_name', + _certificateFile!.bytes!, + filename: _certificateFile!.name, + ), + ); + } else if (_certificateFile!.path != null) { + request.files.add( + await http.MultipartFile.fromPath( + 'certificate_file_name', + _certificateFile!.path!, + filename: _certificateFile!.name, + ), + ); + } + } catch (e) { + ToastHelper.showErrorToast(context, 'Could not attach certificate'); + return; + } + } + + setState(() => _saving = true); + try { + final streamed = await request.send(); + final response = await http.Response.fromStream(streamed); + if (response.statusCode == 403 || response.statusCode == 401) { + await _api.clearLocalStorageAndRedirect(); + return; + } + final body = jsonDecode(response.body); + if (response.statusCode == 200 && + body is Map && + body['status'] == 'success') { + ToastHelper.showSuccessToast( + context, + isUpdate ? 'Partner updated' : 'Partner created', + ); + if (isUpdate && mounted) { + context.go(AppRoutes.agentLst); + return; + } + if (!isUpdate && mounted) { + final data = body['data']; + String? newId; + if (data is Map && data['agent_id'] != null) { + newId = data['agent_id'].toString(); + } + if (newId != null && newId.isNotEmpty) { + context.go(AppRoutes.agentDetailsFor(newId)); + return; + } + ToastHelper.showWarningToast( + context, + 'Partner saved but agent id missing — open from list to set retention', + ); + context.go(AppRoutes.agentLst); + return; + } + } else { + final msg = body is Map ? body['data']?.toString() : response.body; + ToastHelper.showErrorToast(context, msg ?? 'Save failed'); + } + } catch (e) { + ToastHelper.showErrorToast(context, 'Save failed'); + } finally { + if (mounted) setState(() => _saving = false); + } + } + + Map? _selectedSalesExecutiveMap() { + if (_selectedSalesExecutiveId == null) return null; + for (final e in _salesExecutives) { + if (e['id'].toString() == _selectedSalesExecutiveId.toString()) return e; + } + return null; + } + + @override + Widget build(BuildContext context) { + if (_loading) { + return MainLayout( + title: 'Partner', + body: const Center(child: CircularProgressIndicator()), + ); + } + + final pageTitle = widget.isCreate ? 'New Partner' : 'Edit Partner'; + + return MainLayout( + title: 'Partner', + body: SelectionArea( + child: Container( + color: Colors.white, + width: double.infinity, + child: LayoutBuilder( + builder: (context, constraints) { + final maxBody = 1200.0; + final hPad = ResponsiveLayout.isMobile(context) ? 12.0 : 20.0; + final viewW = constraints.hasBoundedWidth && constraints.maxWidth.isFinite + ? constraints.maxWidth + : MediaQuery.sizeOf(context).width; + final contentW = viewW > maxBody ? maxBody : viewW; + return Align( + alignment: Alignment.topCenter, + child: ConstrainedBox( + constraints: BoxConstraints(maxWidth: maxBody), + child: SingleChildScrollView( + padding: EdgeInsets.symmetric( + horizontal: hPad, + vertical: ResponsiveLayout.isMobile(context) ? 12 : 16, + ), + child: Form( + key: _formKey, + autovalidateMode: AutovalidateMode.disabled, + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + _pageHeaderBar(context, pageTitle), + const SizedBox(height: 12), + _partnerDetailsCard(context, contentW), + const SizedBox(height: 24), + _retentionSection(context), + SizedBox(height: ResponsiveLayout.isMobile(context) ? 24 : 16), + ], + ), + ), + ), + ), + ); + }, + ), + ), + ), + ); + } + + Widget _partnerDetailsCard(BuildContext context, double contentW) { + final pad = ResponsiveLayout.isMobile(context) ? 14.0 : 20.0; + return Container( + width: double.infinity, + decoration: BoxDecoration( + color: const Color(0xFFE6F4F1), + borderRadius: BorderRadius.circular(12), + ), + padding: EdgeInsets.all(pad), + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Text( + 'Partner details', + style: GoogleFonts.poppins( + fontSize: 14, + fontWeight: FontWeight.w600, + color: const Color(0xFF1E293B), + ), + ), + const SizedBox(height: 16), + _partnerRows(context, contentW), + const SizedBox(height: 20), + Align( + alignment: Alignment.centerRight, + child: FilledButton( + onPressed: (_saving || _savingAllRetention) ? null : _savePartnerDetails, + style: FilledButton.styleFrom( + backgroundColor: const Color(0xFF2E7D6E), + padding: const EdgeInsets.symmetric(horizontal: 28, vertical: 12), + ), + child: _saving + ? const SizedBox( + width: 20, + height: 20, + child: CircularProgressIndicator( + strokeWidth: 2, + color: Colors.white, + ), + ) + : const Text('Save'), + ), + ), + ], + ), + ); + } + + /// Same pattern as [PayOutDetails] — list-style back control + title. + Widget _pageHeaderBar(BuildContext context, String pageTitle) { + return SizedBox( + height: 40, + width: double.infinity, + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Tooltip( + message: 'Back', + child: IconButton( + icon: const Icon( + Icons.arrow_left_sharp, + size: 25, + color: Color(0xFF425B5B), + ), + onPressed: () => context.go(AppRoutes.agentLst), + splashRadius: 18, + hoverColor: Colors.black12, + padding: const EdgeInsets.all(4), + constraints: const BoxConstraints(), + ), + ), + const SizedBox(width: 5), + Expanded( + child: Text( + pageTitle, + style: GoogleFonts.inter( + fontSize: 14, + fontWeight: FontWeight.w500, + color: const Color(0xFF0F172A), + ), + overflow: TextOverflow.ellipsis, + ), + ), + ], + ), + ); + } + + Widget _partnerRows(BuildContext context, double viewportWidth) { + final mobile = ResponsiveLayout.isMobile(context); + final usable = viewportWidth.isFinite ? viewportWidth.clamp(280.0, 1200.0) : 800.0; + final fieldWidth = mobile ? double.infinity : ((usable - 36) / 3).clamp(140.0, 360.0); + + Widget row1 = mobile + ? Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + _labeledField('Full name *', _nameCtrl, _reqName, + width: fieldWidth, keyboardType: TextInputType.name), + const SizedBox(height: 12), + _labeledField( + 'Email *', + _emailCtrl, + _emailVal, + width: fieldWidth, + keyboardType: TextInputType.emailAddress, + ), + const SizedBox(height: 12), + _labeledField( + 'Phone number *', + _mobileCtrl, + _phoneVal, + width: fieldWidth, + keyboardType: TextInputType.phone, + inputFormatters: _phoneInputFormatters, + ), + ], + ) + : Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + child: _labeledField('Full name *', _nameCtrl, _reqName, + width: fieldWidth, keyboardType: TextInputType.name), + ), + const SizedBox(width: 16), + Expanded( + child: _labeledField( + 'Email *', + _emailCtrl, + _emailVal, + width: fieldWidth, + keyboardType: TextInputType.emailAddress, + ), + ), + const SizedBox(width: 16), + Expanded( + child: _labeledField( + 'Phone number *', + _mobileCtrl, + _phoneVal, + width: fieldWidth, + keyboardType: TextInputType.phone, + inputFormatters: _phoneInputFormatters, + ), + ), + ], + ); + + Widget row2 = mobile + ? Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + _labeledField( + 'Partner Id *', + _codeCtrl, + _reqCode, + width: fieldWidth, + inputFormatters: [ + FilteringTextInputFormatter.allow(RegExp(r'[a-zA-Z0-9]')), + ], + ), + const SizedBox(height: 12), + _salesDropdown(context, fieldWidth), + const SizedBox(height: 12), + _labeledField( + 'Address *', + _addressCtrl, + _reqAddress, + width: fieldWidth, + keyboardType: TextInputType.streetAddress, + ), + const SizedBox(height: 12), + _certificateColumn(context), + ], + ) + : Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + child: _labeledField( + 'Partner Id *', + _codeCtrl, + _reqCode, + width: fieldWidth, + inputFormatters: [ + FilteringTextInputFormatter.allow(RegExp(r'[a-zA-Z0-9]')), + ], + ), + ), + const SizedBox(width: 12), + Expanded(child: _salesDropdown(context, fieldWidth)), + const SizedBox(width: 12), + Expanded( + child: _labeledField( + 'Address *', + _addressCtrl, + _reqAddress, + width: fieldWidth, + keyboardType: TextInputType.streetAddress, + ), + ), + const SizedBox(width: 12), + Expanded(child: _certificateColumn(context)), + ], + ); + + return Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + row1, + const SizedBox(height: 16), + row2, + ], + ); + } + + String? _reqName(String? v) => Validators.requiredField(v, 'Name'); + + String? _reqCode(String? v) => Validators.requiredField(v, 'Partner Id'); + + String? _reqAddress(String? v) => Validators.requiredField(v, 'Address'); + + String? _emailVal(String? v) { + final email = (v ?? '').trim(); + final phone = _mobileCtrl.text.trim(); + if (email.isEmpty && phone.isEmpty) { + return 'Enter email or phone number'; + } + if (email.isEmpty && phone.isNotEmpty) return null; + return Validators.email(email, 'Email'); + } + + String? _phoneVal(String? v) { + final phone = (v ?? '').trim(); + final email = _emailCtrl.text.trim(); + if (email.isEmpty && phone.isEmpty) { + return 'Enter email or phone number'; + } + if (phone.isEmpty && email.isNotEmpty) return null; + if (!RegExp(r'^[0-9]+$').hasMatch(phone)) { + return 'Phone must contain only digits'; + } + if (phone.length != 10) { + return 'Enter a valid 10-digit phone number'; + } + return null; + } + + Widget _labeledField( + String label, + TextEditingController c, + String? Function(String?)? validator, { + required double width, + TextInputType? keyboardType, + List? inputFormatters, + }) { + final w = width == double.infinity ? null : width; + final mq = MediaQuery.sizeOf(context).width; + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(label, style: _labelStyle), + const SizedBox(height: 8), + SizedBox( + width: w ?? double.infinity, + child: ThemedFormField( + controller: c, + validator: validator, + keyboardType: keyboardType, + inputFormatters: inputFormatters, + borderColor: const Color(0xFFE2E8F0), + highlightColor: const Color(0xFF50A398), + txtwidth: w ?? mq, + ), + ), + ], + ); + } + + Widget _salesDropdown(BuildContext context, double fieldWidth) { + final w = fieldWidth == double.infinity ? null : fieldWidth; + final mq = MediaQuery.sizeOf(context).width; + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('Sales executive *', style: _labelStyle), + const SizedBox(height: 8), + SizedBox( + width: w ?? double.infinity, + child: DropdownSearch>( + key: _seKey, + selectedItem: _selectedSalesExecutiveMap(), + items: (f, i) => _salesExecutives, + itemAsString: (m) => (m['name'] ?? '').toString(), + compareFn: (a, b) => a['id'] == b['id'], + onChanged: (v) { + setState(() => _selectedSalesExecutiveId = v?['id']); + }, + decoratorProps: DropDownDecoratorProps( + decoration: AppInputDecorations.dropdownDecoration( + label: 'Select executive', + ).copyWith( + filled: true, + fillColor: Colors.white, + isDense: true, + contentPadding: const EdgeInsets.symmetric(horizontal: 8, vertical: 6), + ), + ), + popupProps: PopupProps.menu( + showSearchBox: true, + fit: FlexFit.loose, + constraints: const BoxConstraints(maxHeight: 280), + menuProps: const MenuProps( + backgroundColor: Colors.white, + ), + searchFieldProps: TextFieldProps( + decoration: InputDecoration( + hintText: 'Search…', + contentPadding: const EdgeInsets.all(8), + ), + ), + ), + ), + ), + ], + ); + } + + Widget _certificateColumn(BuildContext context) { + return LayoutBuilder( + builder: (context, constraints) { + final w = constraints.maxWidth; + final uploadW = w.isFinite && w > 0 ? w : 240.0; + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('Upload certificate', style: _labelStyle), + const SizedBox(height: 8), + ThemedUploadField( + hintText: _certificateLabel ?? 'Upload document', + txtwidth: uploadW, + txtheight: 45, + borderColor: const Color(0xFFE2E8F0), + onFileSelected: (name, file) { + setState(() { + _certificateFile = file; + _certificateLabel = name; + }); + }, + ), + if ((_certificateFile != null || _certificateUrlFromApi != null) && + _loadedAgentPk != null) ...[ + const SizedBox(height: 6), + TextButton( + onPressed: () { + _api.downloadFile( + apiUrl: 'agent/downloadAgentCertificateFile?agent_id=$_loadedAgentPk', + apiId: _loadedAgentPk, + localFile: _certificateFile, + fileName: _certificateLabel, + ); + }, + child: const Text('Download certificate'), + ), + ], + ], + ); + }, + ); + } + + Widget _retentionSection(BuildContext context) { + final mobile = ResponsiveLayout.isMobile(context); + return Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + if (mobile) + Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Text( + 'Retention rate by vehicle type', + style: GoogleFonts.poppins( + fontSize: 14, + fontWeight: FontWeight.w600, + color: const Color(0xFF1E293B), + ), + ), + const SizedBox(height: 10), + ThemedSearchField( + hintText: 'Search vehicle type', + backgroundColor: Colors.white, + txtHeight: 36, + txtwidth: double.infinity, + controller: _retentionSearchCtrl, + onChanged: (_) => setState(() {}), + ), + ], + ) + else + Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Expanded( + child: Text( + 'Retention rate by vehicle type', + style: GoogleFonts.poppins( + fontSize: 14, + fontWeight: FontWeight.w600, + color: const Color(0xFF1E293B), + ), + ), + ), + SizedBox( + width: 220, + child: ThemedSearchField( + hintText: 'Search vehicle type', + backgroundColor: Colors.white, + txtHeight: 34, + controller: _retentionSearchCtrl, + onChanged: (_) => setState(() {}), + ), + ), + ], + ), + const SizedBox(height: 12), + if (mobile) + _retentionMobileList(context) + else + LayoutBuilder( + builder: (context, constraints) { + final parentW = constraints.maxWidth.isFinite + ? constraints.maxWidth + : MediaQuery.sizeOf(context).width; + final screenH = MediaQuery.sizeOf(context).height; + final viewportH = math.min(520.0, math.max(240.0, screenH * 0.42)); + + final tableColumn = Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + _retentionTableHeader(), + ..._filteredVehicleTypes.map(_retentionDataRow), + ], + ); + + return ClipRRect( + borderRadius: BorderRadius.circular(8), + child: Container( + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(8), + border: Border.all(color: const Color(0xFFE2E8F0)), + ), + child: SizedBox( + height: viewportH, + width: double.infinity, + child: Scrollbar( + thumbVisibility: true, + child: SingleChildScrollView( + scrollDirection: Axis.vertical, + primary: false, + child: ConstrainedBox( + constraints: BoxConstraints(minWidth: parentW), + child: tableColumn, + ), + ), + ), + ), + ), + ); + }, + ), + const SizedBox(height: 12), + _retentionSaveAllButtonBottom(context), + ], + ); + } + + Widget _retentionSaveAllButtonBottom(BuildContext context) { + final mobile = ResponsiveLayout.isMobile(context); + return Align( + alignment: mobile ? Alignment.center : Alignment.centerRight, + child: FilledButton( + onPressed: _savingAllRetention + ? null + : (_retentionTableEnabled && !_retentionActionsBusy) + ? _saveAllRetentionRates + : null, + style: FilledButton.styleFrom( + backgroundColor: const Color(0xFF2E7D6E), + padding: EdgeInsets.symmetric( + horizontal: mobile ? 20 : 24, + vertical: 12, + ), + ), + child: _savingAllRetention + ? const SizedBox( + width: 20, + height: 20, + child: CircularProgressIndicator( + strokeWidth: 2, + color: Colors.white, + ), + ) + : const Text('Save all retention rates'), + ), + ); + } + + /// Stacked cards on narrow screens (no forced horizontal scroll). + Widget _retentionMobileList(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + for (final vt in _filteredVehicleTypes) _retentionMobileCard(vt), + ], + ); + } + + Widget _retentionMobileCard(Map vt) { + final id = int.tryParse(vt['id'].toString()) ?? 0; + final name = (vt['vehicle_type'] ?? '-').toString(); + final ctrl = _rateControllers[id]; + if (ctrl == null) return const SizedBox.shrink(); + + return Padding( + padding: const EdgeInsets.only(bottom: 10), + child: DecoratedBox( + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(8), + border: Border.all(color: const Color(0xFFE2E8F0)), + ), + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 10), + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Text(name, style: _headerStyle), + const SizedBox(height: 8), + TextField( + controller: ctrl, + keyboardType: const TextInputType.numberWithOptions(decimal: true), + inputFormatters: _retentionPercentFormatters, + decoration: InputDecoration( + isDense: true, + filled: true, + fillColor: Colors.white, + hintText: '0–100%', + border: OutlineInputBorder(borderRadius: BorderRadius.circular(6)), + contentPadding: const EdgeInsets.symmetric(horizontal: 10, vertical: 10), + ), + ), + const SizedBox(height: 8), + Align( + alignment: Alignment.centerRight, + child: TextButton( + onPressed: (_retentionTableEnabled && !_retentionActionsBusy) + ? () => _saveRowRetention(id) + : null, + style: TextButton.styleFrom( + foregroundColor: const Color(0xFF2E7D6E), + backgroundColor: const Color(0xFFE8ECF0), + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(20), + ), + ), + child: const Text('UPDATE'), + ), + ), + ], + ), + ), + ), + ); + } + + Widget _retentionTableHeader() { + return Container( + padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 14), + decoration: const BoxDecoration( + color: Color(0xFFEDF2F9), + border: Border(bottom: BorderSide(color: Color(0xFFE2E8F0))), + ), + child: Row( + children: [ + Expanded( + flex: 5, + child: Text( + 'Vehicle type', + style: _headerStyle, + overflow: TextOverflow.ellipsis, + ), + ), + Expanded( + flex: 3, + child: Text( + 'Retention %', + style: _headerStyle, + overflow: TextOverflow.ellipsis, + ), + ), + SizedBox( + width: 88, + child: Text( + 'Action', + style: _headerStyle, + textAlign: TextAlign.center, + overflow: TextOverflow.ellipsis, + ), + ), + ], + ), + ); + } + + Widget _retentionDataRow(Map vt) { + final id = int.tryParse(vt['id'].toString()) ?? 0; + final name = (vt['vehicle_type'] ?? '-').toString(); + final ctrl = _rateControllers[id]; + if (ctrl == null) return const SizedBox.shrink(); + + return Container( + padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 12), + decoration: const BoxDecoration( + color: Colors.white, + border: Border(bottom: BorderSide(color: Color(0xFFE5E7EB))), + ), + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Expanded( + flex: 5, + child: Text( + name, + style: _cellStyle, + overflow: TextOverflow.ellipsis, + maxLines: 2, + ), + ), + Expanded( + flex: 3, + child: TextField( + controller: ctrl, + keyboardType: const TextInputType.numberWithOptions(decimal: true), + inputFormatters: _retentionPercentFormatters, + decoration: InputDecoration( + isDense: true, + filled: true, + fillColor: Colors.white, + hintText: '0–100%', + border: OutlineInputBorder(borderRadius: BorderRadius.circular(6)), + contentPadding: const EdgeInsets.symmetric(horizontal: 8, vertical: 8), + ), + ), + ), + SizedBox( + width: 88, + child: Center( + child: TextButton( + onPressed: (_retentionTableEnabled && !_retentionActionsBusy) + ? () => _saveRowRetention(id) + : null, + style: TextButton.styleFrom( + foregroundColor: const Color(0xFF2E7D6E), + backgroundColor: const Color(0xFFE8ECF0), + minimumSize: Size.zero, + tapTargetSize: MaterialTapTargetSize.shrinkWrap, + padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 6), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(20), + ), + ), + child: const Text('UPDATE', style: TextStyle(fontSize: 11, fontWeight: FontWeight.w600)), + ), + ), + ), + ], + ), + ); + } + + static final _labelStyle = GoogleFonts.poppins( + fontSize: 12, + fontWeight: FontWeight.w500, + color: const Color(0xFF334155), + ); + + static final _headerStyle = GoogleFonts.poppins( + fontSize: 11, + fontWeight: FontWeight.w600, + color: const Color(0xFF1E293B), + ); + + static final _cellStyle = GoogleFonts.inter(fontSize: 12, color: const Color(0xFF0F172A)); +}