diff --git a/.flutter-plugins-dependencies b/.flutter-plugins-dependencies index 64b6122..7d85544 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":"/home/venba/.pub-cache/hosted/pub.dev/connectivity_plus-6.1.5/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"file_picker","path":"/home/venba/.pub-cache/hosted/pub.dev/file_picker-10.3.3/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"firebase_app_check","path":"/home/venba/.pub-cache/hosted/pub.dev/firebase_app_check-0.4.0+1/","native_build":true,"dependencies":["firebase_core"],"dev_dependency":false},{"name":"firebase_auth","path":"/home/venba/.pub-cache/hosted/pub.dev/firebase_auth-6.0.2/","native_build":true,"dependencies":["firebase_core"],"dev_dependency":false},{"name":"firebase_core","path":"/home/venba/.pub-cache/hosted/pub.dev/firebase_core-4.1.0/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"firebase_messaging","path":"/home/venba/.pub-cache/hosted/pub.dev/firebase_messaging-16.0.1/","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.1.0/","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":"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":"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":"file_picker","path":"/home/venba/.pub-cache/hosted/pub.dev/file_picker-10.3.3/","native_build":true,"dependencies":["flutter_plugin_android_lifecycle"],"dev_dependency":false},{"name":"firebase_app_check","path":"/home/venba/.pub-cache/hosted/pub.dev/firebase_app_check-0.4.0+1/","native_build":true,"dependencies":["firebase_core"],"dev_dependency":false},{"name":"firebase_auth","path":"/home/venba/.pub-cache/hosted/pub.dev/firebase_auth-6.0.2/","native_build":true,"dependencies":["firebase_core"],"dev_dependency":false},{"name":"firebase_core","path":"/home/venba/.pub-cache/hosted/pub.dev/firebase_core-4.1.0/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"firebase_messaging","path":"/home/venba/.pub-cache/hosted/pub.dev/firebase_messaging-16.0.1/","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.30/","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.0.5/","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.18/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"shared_preferences_android","path":"/home/venba/.pub-cache/hosted/pub.dev/shared_preferences_android-2.4.12/","native_build":true,"dependencies":[],"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":"file_picker","path":"/home/venba/.pub-cache/hosted/pub.dev/file_picker-10.3.3/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"firebase_app_check","path":"/home/venba/.pub-cache/hosted/pub.dev/firebase_app_check-0.4.0+1/","native_build":true,"dependencies":["firebase_core"],"dev_dependency":false},{"name":"firebase_auth","path":"/home/venba/.pub-cache/hosted/pub.dev/firebase_auth-6.0.2/","native_build":true,"dependencies":["firebase_core"],"dev_dependency":false},{"name":"firebase_core","path":"/home/venba/.pub-cache/hosted/pub.dev/firebase_core-4.1.0/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"firebase_messaging","path":"/home/venba/.pub-cache/hosted/pub.dev/firebase_messaging-16.0.1/","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.1.0/","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":"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":"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":"file_picker","path":"/home/venba/.pub-cache/hosted/pub.dev/file_picker-10.3.3/","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":"url_launcher_linux","path":"/home/venba/.pub-cache/hosted/pub.dev/url_launcher_linux-3.2.1/","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":"file_picker","path":"/home/venba/.pub-cache/hosted/pub.dev/file_picker-10.3.3/","native_build":false,"dependencies":[],"dev_dependency":false},{"name":"firebase_auth","path":"/home/venba/.pub-cache/hosted/pub.dev/firebase_auth-6.0.2/","native_build":true,"dependencies":["firebase_core"],"dev_dependency":false},{"name":"firebase_core","path":"/home/venba/.pub-cache/hosted/pub.dev/firebase_core-4.1.0/","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":"url_launcher_windows","path":"/home/venba/.pub-cache/hosted/pub.dev/url_launcher_windows-3.1.4/","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":"file_picker","path":"/home/venba/.pub-cache/hosted/pub.dev/file_picker-10.3.3/","dependencies":[],"dev_dependency":false},{"name":"firebase_app_check_web","path":"/home/venba/.pub-cache/hosted/pub.dev/firebase_app_check_web-0.2.0+16/","dependencies":["firebase_core_web"],"dev_dependency":false},{"name":"firebase_auth_web","path":"/home/venba/.pub-cache/hosted/pub.dev/firebase_auth_web-6.0.2/","dependencies":["firebase_core_web"],"dev_dependency":false},{"name":"firebase_core_web","path":"/home/venba/.pub-cache/hosted/pub.dev/firebase_core_web-3.1.0/","dependencies":[],"dev_dependency":false},{"name":"firebase_messaging_web","path":"/home/venba/.pub-cache/hosted/pub.dev/firebase_messaging_web-4.0.1/","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.0.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":"shared_preferences_web","path":"/home/venba/.pub-cache/hosted/pub.dev/shared_preferences_web-2.4.3/","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":"file_picker","dependencies":["flutter_plugin_android_lifecycle"]},{"name":"firebase_app_check","dependencies":["firebase_app_check_web","firebase_core"]},{"name":"firebase_app_check_web","dependencies":["firebase_core","firebase_core_web"]},{"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":"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":"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":"2025-11-06 18:27:55.432179","version":"3.32.1","swift_package_manager_enabled":{"ios":false,"macos":false}} \ No newline at end of file +{"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":"file_picker","path":"/home/venba/.pub-cache/hosted/pub.dev/file_picker-10.3.3/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"firebase_app_check","path":"/home/venba/.pub-cache/hosted/pub.dev/firebase_app_check-0.4.0+1/","native_build":true,"dependencies":["firebase_core"],"dev_dependency":false},{"name":"firebase_auth","path":"/home/venba/.pub-cache/hosted/pub.dev/firebase_auth-6.0.2/","native_build":true,"dependencies":["firebase_core"],"dev_dependency":false},{"name":"firebase_core","path":"/home/venba/.pub-cache/hosted/pub.dev/firebase_core-4.1.0/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"firebase_messaging","path":"/home/venba/.pub-cache/hosted/pub.dev/firebase_messaging-16.0.1/","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.1.0/","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":"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":"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":"file_picker","path":"/home/venba/.pub-cache/hosted/pub.dev/file_picker-10.3.3/","native_build":true,"dependencies":["flutter_plugin_android_lifecycle"],"dev_dependency":false},{"name":"firebase_app_check","path":"/home/venba/.pub-cache/hosted/pub.dev/firebase_app_check-0.4.0+1/","native_build":true,"dependencies":["firebase_core"],"dev_dependency":false},{"name":"firebase_auth","path":"/home/venba/.pub-cache/hosted/pub.dev/firebase_auth-6.0.2/","native_build":true,"dependencies":["firebase_core"],"dev_dependency":false},{"name":"firebase_core","path":"/home/venba/.pub-cache/hosted/pub.dev/firebase_core-4.1.0/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"firebase_messaging","path":"/home/venba/.pub-cache/hosted/pub.dev/firebase_messaging-16.0.1/","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.30/","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.0.5/","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.18/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"shared_preferences_android","path":"/home/venba/.pub-cache/hosted/pub.dev/shared_preferences_android-2.4.12/","native_build":true,"dependencies":[],"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":"file_picker","path":"/home/venba/.pub-cache/hosted/pub.dev/file_picker-10.3.3/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"firebase_app_check","path":"/home/venba/.pub-cache/hosted/pub.dev/firebase_app_check-0.4.0+1/","native_build":true,"dependencies":["firebase_core"],"dev_dependency":false},{"name":"firebase_auth","path":"/home/venba/.pub-cache/hosted/pub.dev/firebase_auth-6.0.2/","native_build":true,"dependencies":["firebase_core"],"dev_dependency":false},{"name":"firebase_core","path":"/home/venba/.pub-cache/hosted/pub.dev/firebase_core-4.1.0/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"firebase_messaging","path":"/home/venba/.pub-cache/hosted/pub.dev/firebase_messaging-16.0.1/","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.1.0/","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":"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":"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":"file_picker","path":"/home/venba/.pub-cache/hosted/pub.dev/file_picker-10.3.3/","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":"url_launcher_linux","path":"/home/venba/.pub-cache/hosted/pub.dev/url_launcher_linux-3.2.1/","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":"file_picker","path":"/home/venba/.pub-cache/hosted/pub.dev/file_picker-10.3.3/","native_build":false,"dependencies":[],"dev_dependency":false},{"name":"firebase_auth","path":"/home/venba/.pub-cache/hosted/pub.dev/firebase_auth-6.0.2/","native_build":true,"dependencies":["firebase_core"],"dev_dependency":false},{"name":"firebase_core","path":"/home/venba/.pub-cache/hosted/pub.dev/firebase_core-4.1.0/","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":"url_launcher_windows","path":"/home/venba/.pub-cache/hosted/pub.dev/url_launcher_windows-3.1.4/","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":"file_picker","path":"/home/venba/.pub-cache/hosted/pub.dev/file_picker-10.3.3/","dependencies":[],"dev_dependency":false},{"name":"firebase_app_check_web","path":"/home/venba/.pub-cache/hosted/pub.dev/firebase_app_check_web-0.2.0+16/","dependencies":["firebase_core_web"],"dev_dependency":false},{"name":"firebase_auth_web","path":"/home/venba/.pub-cache/hosted/pub.dev/firebase_auth_web-6.0.2/","dependencies":["firebase_core_web"],"dev_dependency":false},{"name":"firebase_core_web","path":"/home/venba/.pub-cache/hosted/pub.dev/firebase_core_web-3.1.0/","dependencies":[],"dev_dependency":false},{"name":"firebase_messaging_web","path":"/home/venba/.pub-cache/hosted/pub.dev/firebase_messaging_web-4.0.1/","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.0.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":"shared_preferences_web","path":"/home/venba/.pub-cache/hosted/pub.dev/shared_preferences_web-2.4.3/","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":"file_picker","dependencies":["flutter_plugin_android_lifecycle"]},{"name":"firebase_app_check","dependencies":["firebase_app_check_web","firebase_core"]},{"name":"firebase_app_check_web","dependencies":["firebase_core","firebase_core_web"]},{"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":"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":"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":"2025-11-13 17:56:47.541149","version":"3.32.1","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 5270078..84b5c49 100644 --- a/lib/core/routing/app_router.dart +++ b/lib/core/routing/app_router.dart @@ -5,7 +5,7 @@ import 'package:nhance_partner/core/routing/routes.dart'; import 'package:nhance_partner/presentation/screens/Enquiry/enquiryList.dart'; import 'package:nhance_partner/presentation/screens/UserManagement/Profile/profile_web.dart'; import 'package:nhance_partner/presentation/screens/dashboard/dashboard.dart'; -import 'package:nhance_partner/presentation/screens/handler/enquiryList.dart'; + import '../../data/services/auth_service.dart'; import '../../presentation/providers/manager_provider.dart'; import '../../presentation/screens/Enquiry/enquiry/tabs.dart'; @@ -18,9 +18,12 @@ import '../../presentation/screens/UserManagement/Agent/agentList.dart'; import '../../presentation/screens/UserManagement/Profile/profile_mobile.dart'; import '../../presentation/screens/UserManagement/Staff/staff.dart'; import '../../presentation/screens/UserManagement/Staff/staffList.dart'; +import '../../presentation/screens/handler/enquiryList.dart'; +import '../../presentation/screens/handler/enquiryListOld.dart'; import '../../presentation/screens/home/home_screen.dart'; import '../../presentation/screens/login/login_screen.dart'; import '../../presentation/screens/splash/splash_screen.dart'; +import '../../presentation/screens/staff/Enquiry/enquiry_inline_list.dart'; import '../../presentation/screens/staff/Enquiry/enquiry_list.dart'; import '../../presentation/screens/staff/policy/policy.dart'; import '../../presentation/screens/staff/policy/policy_list.dart'; @@ -157,11 +160,13 @@ final GoRouter appRouter = GoRouter( GoRoute( path: AppRoutes.enquiryForStaff, - builder: (context, state) => const EnquiryStaff(), + builder: (context, state) => const EnquiryListStaffInline(), + // builder: (context, state) => const EnquiryStaff(), ), GoRoute( path: AppRoutes.enquiryHandlerLst, - builder: (context, state) => const EnquiryHandler(), + // builder: (context, state) => const EnquiryHandler(), + builder: (context, state) => EnquiryListHandler(), ), GoRoute( path: AppRoutes.allStaffAttendance, diff --git a/lib/core/services/api_service.dart b/lib/core/services/api_service.dart index 47f44ce..032ad7a 100644 --- a/lib/core/services/api_service.dart +++ b/lib/core/services/api_service.dart @@ -596,13 +596,14 @@ class ApiService { final String query; - if (role == 'manager' || role == 'handler') { - query = 'manager_id=$id'; - } - // else if (role == 'handler') { - // query = 'handler_id=$id'; + // if (role == 'manager' || role == 'handler') { + // query = 'manager_id=$id'; // } - else if (role == 'staff') { + if (role == 'manager') { + query = 'manager_id=$id'; + } else if (role == 'handler') { + query = 'handler_id=$id'; + } else if (role == 'staff') { query = 'staff_id=$id'; } else { query = 'agent_id=$id'; @@ -991,6 +992,8 @@ class ApiService { url = Uri.parse('${Env.apiUrl}master/getClaimMaster'); } else if (val == 'Endorsement') { url = Uri.parse('${Env.apiUrl}master/getEndorsementMaster'); + } else if (val == 'Broker') { + url = Uri.parse('${Env.apiUrl}master/getAllBrokers'); } final headers = { diff --git a/lib/data/utils/Pagination.dart b/lib/data/utils/Pagination.dart index de70a15..a7fe744 100644 --- a/lib/data/utils/Pagination.dart +++ b/lib/data/utils/Pagination.dart @@ -50,68 +50,73 @@ class PaginationControls extends StatelessWidget { final int totalPages = (totalItems / itemsPerPage).ceil(); final List pages = _visiblePages(totalPages, currentPage); - return Row( - mainAxisAlignment: MainAxisAlignment.end, - children: [ - // Dropdown for rows per page - DropdownButton( - value: itemsPerPage, - items: [5, 10, 15, 20, 50].map((int value) { - return DropdownMenuItem( - value: value, - child: Text('$value', style: GoogleFonts.poppins(fontSize: 15)), - ); - }).toList(), - onChanged: (newValue) { - if (newValue != null) { - onItemsPerPageChanged(newValue); - onPageChanged(1); - } - }, - ), + return Container( + // color: Colors.yellow.shade100, + height: 30, + child: Row( + mainAxisAlignment: MainAxisAlignment.end, + mainAxisSize: MainAxisSize.min, + children: [ + // Dropdown for rows per page + DropdownButton( + value: itemsPerPage, + items: [5, 10, 15, 20, 50].map((int value) { + return DropdownMenuItem( + value: value, + child: Text('$value', style: GoogleFonts.inter(fontSize: 12)), + ); + }).toList(), + onChanged: (newValue) { + if (newValue != null) { + onItemsPerPageChanged(newValue); + onPageChanged(1); + } + }, + ), - // Previous Button - IconButton( - onPressed: currentPage > 1 - ? () => onPageChanged(currentPage - 1) - : null, - icon: const Icon(Icons.chevron_left), - ), + // Previous Button + IconButton( + onPressed: currentPage > 1 + ? () => onPageChanged(currentPage - 1) + : null, + icon: const Icon(Icons.chevron_left, size: 18), + ), - // Page buttons with ellipsis - for (final i in pages) - if (i == -1) - const Padding( - padding: EdgeInsets.symmetric(horizontal: 4), - child: Text("..."), - ) - else - Padding( - padding: const EdgeInsets.symmetric(horizontal: 4), - child: ElevatedButton( - style: ElevatedButton.styleFrom( - backgroundColor: currentPage == i - ? const Color(0xFFD4F8F3) - : const Color(0xFFEDF6F5), - foregroundColor: currentPage == i - ? Colors.black - : Colors.grey, - minimumSize: const Size(36, 36), - padding: EdgeInsets.zero, + // Page buttons with ellipsis + for (final i in pages) + if (i == -1) + const Padding( + padding: EdgeInsets.symmetric(horizontal: 2), + child: Text("..."), + ) + else + Padding( + padding: const EdgeInsets.symmetric(horizontal: 2), + child: ElevatedButton( + style: ElevatedButton.styleFrom( + backgroundColor: currentPage == i + ? const Color(0xFFD4F8F3) + : const Color(0xFFEDF6F5), + foregroundColor: currentPage == i + ? Colors.black + : Colors.grey, + minimumSize: const Size(26, 26), + padding: EdgeInsets.zero, + ), + onPressed: () => onPageChanged(i), + child: Text(i.toString()), ), - onPressed: () => onPageChanged(i), - child: Text(i.toString()), ), - ), - // Next Button - IconButton( - onPressed: currentPage < totalPages - ? () => onPageChanged(currentPage + 1) - : null, - icon: const Icon(Icons.chevron_right), - ), - ], + // Next Button + IconButton( + onPressed: currentPage < totalPages + ? () => onPageChanged(currentPage + 1) + : null, + icon: const Icon(Icons.chevron_right, size: 18), + ), + ], + ), ); } } diff --git a/lib/presentation/layouts/main_layout.dart b/lib/presentation/layouts/main_layout.dart index e73bdd3..0f27a73 100644 --- a/lib/presentation/layouts/main_layout.dart +++ b/lib/presentation/layouts/main_layout.dart @@ -33,7 +33,7 @@ class MainLayout extends ConsumerStatefulWidget { class MainLayoutState extends ConsumerState { dynamic role; ApiService apiService = ApiService(); - + bool isDrawerOpen = true; @override void initState() { super.initState(); @@ -93,7 +93,12 @@ class MainLayoutState extends ConsumerState { appBar: TopBar( title: widget.title, onMenuPressed: () { - Scaffold.of(context).openDrawer(); + // Scaffold.of(context).openDrawer(); + // Scaffold.of(context).openDrawer(); + + setState(() { + isDrawerOpen = !isDrawerOpen; + }); }, onLogout: () async { debugPrint("Logout tapped"); @@ -118,7 +123,7 @@ class MainLayoutState extends ConsumerState { items: [ PopupMenuItem( enabled: false, - child: SizedBox(width: 500, child: ProfilePopUp()), + child: SizedBox(width: 80, child: ProfilePopUp()), ), ], ); @@ -130,13 +135,17 @@ class MainLayoutState extends ConsumerState { ), body: Row( children: [ - const SizedBox( - width: 100, // fixed width for drawer - child: DrawerMenu(), + AnimatedContainer( + duration: const Duration(milliseconds: 250), + width: isDrawerOpen ? 100 : 0, // 👈 adjust width + child: isDrawerOpen + ? const DrawerMenu() + : const SizedBox.shrink(), // hides drawer ), + Expanded( child: Container( - padding: EdgeInsets.all(2.0), + padding: const EdgeInsets.all(8.0), color: Colors.white, child: Column(children: [Expanded(child: widget.body)]), ), @@ -144,6 +153,23 @@ class MainLayoutState extends ConsumerState { ], ), ), + + // body: Row( + // children: [ + // const SizedBox( + // width: 100, // fixed width for drawer + // child: DrawerMenu(), + // ), + // Expanded( + // child: Container( + // padding: EdgeInsets.all(2.0), + // color: Colors.white, + // child: Column(children: [Expanded(child: widget.body)]), + // ), + // ), + // ], + // ), + // ), ); } } diff --git a/lib/presentation/providers/manager_provider.dart b/lib/presentation/providers/manager_provider.dart index 9b1f74b..5e795be 100644 --- a/lib/presentation/providers/manager_provider.dart +++ b/lib/presentation/providers/manager_provider.dart @@ -5,6 +5,7 @@ final managerIdProvider = StateProvider((ref) => null); final userIdProvider = StateProvider((ref) => null); final enquiryIdProvider = StateProvider((ref) => null); final navFromEnqStaffProvider = StateProvider((ref) => null); +final enqBrokerNameProvider = StateProvider((ref) => null); final staffIndiviualAttendanceIdProvider = StateProvider( (ref) => null, ); diff --git a/lib/presentation/screens/Enquiry/enquiry/enquiry_tab.dart b/lib/presentation/screens/Enquiry/enquiry/enquiry_tab.dart index 998c3e6..334d585 100644 --- a/lib/presentation/screens/Enquiry/enquiry/enquiry_tab.dart +++ b/lib/presentation/screens/Enquiry/enquiry/enquiry_tab.dart @@ -86,12 +86,12 @@ class EnquiryTabState extends ConsumerState { String? selectedInsurer; String? selectedAgent; - Map controllers = {}; String? _token; dynamic userId; dynamic managerId; dynamic role; + Map controllers = {}; List> getVehicleTypeData = []; List> filteredVechicleData = []; @@ -733,42 +733,6 @@ class EnquiryTabState extends ConsumerState { } } - // Widget buildResponsiveUploadField({ - // required String label, - // required String? hintText, - // required void Function(String fileName, dynamic file) onFileSelected, - // }) - // { - // final isMobile = ResponsiveLayout.isMobile(context); - // - // final uploadWidget = ThemedUploadField( - // hintText: hintText ?? "Upload Document", - // txtwidth: MediaQuery.of(context).size.width * 0.26, - // onFileSelected: onFileSelected, - // ); - // - // if (isMobile) { - // return Column( - // crossAxisAlignment: CrossAxisAlignment.start, - // children: [ - // Text(label, style: _textStyle), - // const SizedBox(height: 8), - // uploadWidget, - // const SizedBox(height: 16), - // ], - // ); - // } else { - // return Row( - // mainAxisAlignment: MainAxisAlignment.spaceBetween, - // children: [ - // Expanded(child: Text(label, style: _textStyle)), - // const SizedBox(width: 10), - // uploadWidget, - // ], - // ); - // } - // } - Widget buildName(BuildContext context) { return buildResponsiveField( label: "Insured Name *", diff --git a/lib/presentation/screens/UserManagement/Staff/staff.dart b/lib/presentation/screens/UserManagement/Staff/staff.dart index 2917a34..a6cb406 100644 --- a/lib/presentation/screens/UserManagement/Staff/staff.dart +++ b/lib/presentation/screens/UserManagement/Staff/staff.dart @@ -677,7 +677,8 @@ class StaffState extends ConsumerState { (item) => item['id'].toString() == selectedHandler, orElse: () => {}, ); - final isReadOnly = widget.id != null; + // final isReadOnly = widget.id != null; + final isReadOnly = false; return Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, diff --git a/lib/presentation/screens/handler/enquiryList.dart b/lib/presentation/screens/handler/enquiryList.dart index f366f15..5e5ffcb 100644 --- a/lib/presentation/screens/handler/enquiryList.dart +++ b/lib/presentation/screens/handler/enquiryList.dart @@ -1,4 +1,5 @@ -// import 'dart:io' as html; +import 'package:file_picker/file_picker.dart'; +import 'package:flutter/services.dart'; import 'package:http/http.dart' as http; import 'package:dropdown_search/dropdown_search.dart'; import 'package:flutter/cupertino.dart'; @@ -7,6 +8,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:go_router/go_router.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:intl/intl.dart'; +import 'package:nhance_partner/presentation/screens/handler/quotationPopUp.dart'; import 'package:shared_preferences/shared_preferences.dart'; import '../../../../core/routing/routes.dart'; @@ -16,34 +18,46 @@ import 'package:universal_html/html.dart' as html; import '../../../../data/utils/Pagination.dart'; +import '../../../core/config/env.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/quotation_staff_proivder.dart'; import '../../providers/userRoleProvider.dart'; +import '../../themes/indicators/customizd_file_upload.dart'; import '../../themes/indicators/export_btn.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/text_field_theme_inline_editor.dart' + hide UpperCaseTextFormatter; import '../../widgets/custom_Stdate_EnDate_Filter.dart'; import '../../widgets/custom_action_popup.dart'; import '../staff/assignStaff.dart'; -class EnquiryHandler extends ConsumerStatefulWidget { - const EnquiryHandler({super.key}); +class EnquiryListHandler extends ConsumerStatefulWidget { + const EnquiryListHandler({super.key}); @override - ConsumerState createState() => EnquiryHandlerState(); + ConsumerState createState() => EnquiryHandlerState(); } -class EnquiryHandlerState extends ConsumerState { +class EnquiryHandlerState extends ConsumerState { int currentPage = 1; int itemsPerPage = 10; late ApiService apiService; dynamic userId; + dynamic idPrimary; dynamic managerId; dynamic dashboardKey; dynamic SelectedStatus; dynamic SelectedStaffId; dynamic handlerId; + String? _token; final _formKey = GlobalKey(); + Map fieldErrors = {}; // List> dataVal = []; List> getStaffData = []; @@ -51,9 +65,112 @@ class EnquiryHandlerState extends ConsumerState { List> filteredData = []; bool isLoading = false; dynamic roleId; + bool _showFilterRow = false; Map controllers = {}; - List tabHeader = ['startDate', 'endDate']; + List tabHeader = [ + 'startDate', + 'endDate', + 'name', + 'email', + 'mobile', + 'code', + 'address', + 'regNo', + 'remarks', + ]; + + Map isEditingRow = {}; + Map> rowControllers = {}; + + //---------------------- Enquiry TAB Initializations Starts -------------------------------- // + + List> getVehicleTypeData = []; + List> filteredVechicleData = []; + + List> getAgentListData = []; + List> filteredAgentData = []; + + List> getInsurersData = []; + List> filteredInsurersData = []; + + String? selectedVehicleType; + String? selectedInsurer; + String? selectedAgent; + PlatformFile? docUploadedRCFile; + PlatformFile? docUploadedIDProof; + PlatformFile? docUploadedPrevPolicy; + + String? selectedRCFile; + String? selectedIdProof; + String? selectedPrevPolicy; + + String? selectedRCFileName; + + String? rcFileUrlFromApi; + String? idProofFileUrlFromApi; + String? prevPolicyFileUrlFromApi; + + String? selectedId; + + Map dataDetails() { + final data = { + "agent_id": ((roleId == 'handler') || (roleId == 'manager')) + ? selectedAgent + : userId, + "name": controllers["name"]?.text, + "mobile": controllers["mobile"]?.text, + "email": controllers["email"]?.text, + "reg_no": controllers["regNo"]?.text, + "vehicle_type_id": selectedVehicleType, + "is_data_created_by_handler": roleId == 'handler' ? '1' : '0', + "is_data_created_by_manager": roleId == 'manager' ? '1' : '0', + // "insurer_id": selectedInsurer, + // "rc_file_name": "rc_doc.pdf", + // "id_proof_file_name": "id_proof.pdf", + // "previous_policy_file_name": "previous_policy.pdf", + "remarks": controllers["remarks"]?.text, + + "assigned_to": selectedStaff, + "insurer_id": selectedInsurer, + "broker_id": selectedBroker, + "manager_id": managerId, + "created_by": userId, + }; + + return data; + } + + final GlobalKey>> dropDownKey = + GlobalKey>>(); + final GlobalKey>> + dropDownKeyInsurer = GlobalKey>>(); + final GlobalKey>> dropDownKeyAgent = + GlobalKey>>(); + //---------------------- Assign Staff starts -------------------------------- // + + final GlobalKey>> + dropDownKeyInsurerEnqAsgn = + GlobalKey>>(); + + final GlobalKey>> dropDownKeyBroker = + GlobalKey>>(); + final GlobalKey>> + dropDownSelectStaffKey = + GlobalKey>>(); + + List> getStaffDetailsDataEnqAsgn = []; + List> filteredStaffDataEnqAsgn = []; + + String? selectedStaff; + String? selectedRegNum; + String? selectedStaffName; + List> getBrokerData = []; + List> filteredBrokerData = []; + String? selectedBroker; + + //---------------------- Assign Staff Ends -------------------------------- // + //---------------------- Enquiry TAB Initializations Ends -------------------------------- // @override void initState() { @@ -64,24 +181,21 @@ class EnquiryHandlerState extends ConsumerState { for (String field in tabHeader) { controllers[field] = TextEditingController(); } + _initializeToken(); Future.microtask(() async { - final id = ref.read(managerIdProvider); + // final id = ref.read(managerIdProvider); roleId = ref.read(userRoleProvider); userId = ref.read(userIdProvider); managerId = ref.read(managerIdProvider); - // dashboardKey = ref.read(dashboardKeyProvider); - // final dashboardStatus = ref.read(dashboardStatusProvider); - // final dashboardStaffId = ref.read(dashboardStaffIdProvider); final prefs = await SharedPreferences.getInstance(); dashboardKey = prefs.getString('dashboardKeyProvider'); final dashboardStatus = prefs.getString('dashboardStatusProvider'); final dashboardStaffId = prefs.getString('dashboardStaffIdProvider'); - // handlerId = ref.read(handlerIdProvider); print('handlerIdENQ - $handlerId'); - print("C72 => r : $roleId | mId: $id | uId: $userId !mID : $managerId "); + print("C72 => r : $roleId | uId: $userId !mID : $managerId "); print('dashboardKey - $dashboardKey'); print('dashboardStatus - $dashboardStatus'); @@ -104,7 +218,28 @@ class EnquiryHandlerState extends ConsumerState { print('ELSE'); getStaffList(managerId, roleId); } + if (managerId != null) { + print('managerId - $managerId'); + getAgentList(managerId); + } + + final userID = ref.watch(userIdProvider); + + print("managerId - $managerId"); + if (userID != null) { + print('hansles'); + getStaffDetailsForEnquiryAssignment(userID); + } }); + + getVehicleType(); + getInsurers(); + getBroker(); + } + + Future _initializeToken() async { + _token = await AuthService.getToken(); + print("APISERTOKEN - $_token"); } void refresh() { @@ -119,28 +254,6 @@ class EnquiryHandlerState extends ConsumerState { } void filterDateRange() { - // print('SelectedStatus - $SelectedStatus'); - // // ✅ Validate the form first - // if (!_formKey.currentState!.validate()) { - // // stop execution if validation fails - // return; - // } - // - // final fromDateText = controllers['startDate']?.text ?? ''; - // final toDateText = controllers['endDate']?.text ?? ''; - // - // // Optional: double-check End >= Start - // final fromDate = DateFormat('dd-MM-yyyy').parse(fromDateText); - // final toDate = DateFormat('dd-MM-yyyy').parse(toDateText); - // - // if (toDate.isBefore(fromDate)) { - // // This is already caught by the validator, but extra safety - // ScaffoldMessenger.of(context).showSnackBar( - // SnackBar(content: Text("End Date cannot be earlier than Start Date")), - // ); - // return; - // } - // ✅ Call your API getStaffList( managerId, @@ -151,6 +264,7 @@ class EnquiryHandlerState extends ConsumerState { } Future refrshfilterDateRange() async { + print('refrshfilterDateRange'); // setState(() async { final prefs = await SharedPreferences.getInstance(); @@ -168,12 +282,116 @@ class EnquiryHandlerState extends ConsumerState { // Reset the FormField validation _formKey.currentState?.reset(); // }); + + final int parsedManagerId = int.tryParse(managerId.toString()) ?? 0; + print('refrshfilterDateRange 1'); getStaffList( - managerId, + // managerId, + parsedManagerId, roleId, SelectedStatus: SelectedStatus ?? '', SelectedStaffId: SelectedStaffId ?? '', ); + print('refrshfilterDateRange 2'); + } + + Future getInsurers() async { + print('Insurers called'); + setState(() { + isLoading = true; + }); + + try { + final response = await apiService.fetchMasterDropDown('Insurers'); + + if (response['status'] == 200) { + print('getInsurers - ${response['data']}'); + setState(() { + getInsurersData = List>.from(response['data']); + print('API Data - $getInsurersData'); + + filteredInsurersData = List.from(getInsurersData); + print('originalData - $filteredInsurersData'); + }); + } else { + getInsurersData = []; + filteredInsurersData = []; + } + } catch (e) { + print('Exception occurred: $e'); + } finally { + setState(() { + isLoading = false; + }); + } + } + + Future getBroker() async { + print('getBroker called'); + setState(() { + isLoading = true; + }); + + try { + final response = await apiService.fetchMasterDropDown('Broker'); + + if (response['status'] == 200) { + print('getBroker - ${response['data']}'); + setState(() { + getBrokerData = List>.from(response['data']); + print('API Data - $getBrokerData'); + + filteredBrokerData = List.from(getBrokerData); + print('originalData - $filteredBrokerData'); + }); + } else { + getBrokerData = []; + filteredBrokerData = []; + } + } catch (e) { + print('Exception occurred: $e'); + } finally { + setState(() { + isLoading = false; + }); + } + } + + Future getStaffDetailsForEnquiryAssignment(int id) async { + print('getStaffDetailsForEnquiryAssignment called By handler'); + setState(() { + isLoading = true; + }); + + try { + // final response = await apiService.fetchStaffUserList(id, role); + final response = await apiService.fetchStaffListForEnquiryAssignDropDown( + id, + roleId, + ); + + if (response['status'] == 'success') { + print('getStaffDetailsForEnquiryAssignment - ${response['data']}'); + setState(() { + getStaffDetailsDataEnqAsgn = List>.from( + response['data'], + ); + print('API Data - $getStaffDetailsDataEnqAsgn'); + + filteredStaffDataEnqAsgn = List.from(getStaffDetailsDataEnqAsgn); + print('originalData - $filteredStaffDataEnqAsgn'); + }); + } else { + getStaffDetailsDataEnqAsgn = []; + filteredStaffDataEnqAsgn = []; + } + } catch (e) { + print('Exception occurred: $e'); + } finally { + setState(() { + isLoading = false; + }); + } } Future getStaffList( @@ -255,67 +473,6 @@ class EnquiryHandlerState extends ConsumerState { } } - // Future getStaffList1( - // int managerId, - // role, { - // String fromDate = '', - // String toDate = '', - // }) async { - // print('C89 => Fns called => $managerId | $role'); - // setState(() { - // isLoading = true; - // }); - // - // try { - // final response = await apiService.fetchPolicyList( - // managerId, - // role, - // fromDate: controllers['startDate']?.text ?? '', - // toDate: controllers['endDate']?.text ?? '', - // ); - // - // print('FromDate : $fromDate'); - // print('ToDate : $toDate'); - // - // if (response['status'] == 'success') { - // final data = response['data']; - // print('C99 => getStaffListData => ${response['data']}'); - // final fromDate = response['from_date'] ?? ''; - // final toDate = response['to_date'] ?? ''; - // - // print('FromDate : $fromDate'); - // print('ToDate : $toDate'); - // setState(() { - // controllers['startDate']?.text = fromDate; - // controllers['endDate']?.text = toDate; - // - // if (data is List) { - // // Already a list of maps - // getStaffData = List>.from(data); - // } else if (data is Map) { - // // Single object, wrap in a list - // getStaffData = [Map.from(data)]; - // } else { - // getStaffData = []; - // } - // // getStaffData = List>.from(response['data']); - // originalData = getStaffData; - // filteredData = List.from(originalData); - // // print('originalData - $getClaimPolicies'); - // }); - // } else { - // getStaffData = []; - // originalData = []; - // } - // } catch (e) { - // print('Exception occurred: $e'); - // } finally { - // setState(() { - // isLoading = false; - // }); - // } - // } - List get _paginatedData { // Sort descending by id first final sortedData = [...filteredData] @@ -407,165 +564,1373 @@ class EnquiryHandlerState extends ConsumerState { ); } - Future handleEdit(item) async { - // Navigator.pop(context); - print('EDITStaff - ${item['id']}'); + Future handleProposalAccept(BuildContext context, String id) async { + final result = showDialog( + context: context, + barrierDismissible: true, // allows tapping outside to close + builder: (ctx) { + return Dialog( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(26), + ), + insetPadding: const EdgeInsets.all(24), + child: Container( + width: 500, + height: 300, + color: Colors.white, + // width: MediaQuery.of(context).size.width * 0.4, + // height: MediaQuery.of(context).size.height * 0.4, + // width: MediaQuery.of(context).size.width * 0.7, + // height: MediaQuery.of(context).size.height * 0.8, + child: QuotationPopUpTab(id: id, onRefresh: refrshfilterDateRange), + ), + ); + }, + ); - final prefs = await SharedPreferences.getInstance(); - - await prefs.remove('enqAgentDataId'); - - final id = item['id'].toString(); - // ✅ Save the new id - await prefs.setString('enqAgentDataId', id.toString()); - ref.read(enquiryIdProvider.notifier).state = id; - - context.go(AppRoutes.tabEnquiry); + print("Dialog result: $result"); + (); } - // List _buildPopupMenuActions( - // BuildContext context, - // dynamic data, - // id, - // regNum, - // ) { - // return [ - // if (roleId == 'manager' && data['status'] == 'Awaiting Quotation') ...[ - // Material( - // color: Colors.transparent, - // child: InkWell( - // onTap: () { - // Navigator.pop(context); - // showDialog( - // context: context, - // builder: (ctx) => AssignStaffDialog( - // enquiryPrimaryId: id, - // regNum: regNum, - // userId: 1, - // onSubmit: (value) { - // debugPrint("New assignY: $value"); - // refresh(); - // }, - // ), - // ); - // }, - // hoverColor: Color(0xFFE3F1F0), - // splashColor: Color(0xFFE3F1F0), - // borderRadius: BorderRadius.circular(6), - // child: Padding( - // padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4), - // child: Row( - // // mainAxisSize: MainAxisSize.min, - // children: [ - // Image.asset( - // "assets/miscellaneous/image_3.png", - // // height: 45, - // // width: 15, - // ), - // - // SizedBox(width: 10), - // Text('Assign Staff'), - // ], - // ), - // ), - // ), - // ), - // ], - // - // Material( - // color: Colors.transparent, - // child: InkWell( - // onTap: () async { - // Navigator.pop(context); - // final prefs = await SharedPreferences.getInstance(); - // - // // ✅ Remove old value (if any) - // await prefs.remove('enqStaffDataId'); - // - // // ✅ Save the new id - // await prefs.setString('enqStaffDataId', id.toString()); - // - // // Read it back if needed - // // final dynamic? enqStaffDataId = prefs.getString('enqStaffDataId'); - // - // // ✅ Update provider too - // // ref.read(quotationStaffIdProvider.notifier).state = enqStaffDataId; - // ref.read(quotationStaffIdProvider.notifier).state = id; - // - // context.go(AppRoutes.quotation); - // }, - // hoverColor: Color(0xFFE3F1F0), - // splashColor: Color(0xFFE3F1F0), - // borderRadius: BorderRadius.circular(6), - // child: Padding( - // padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4), - // child: Row( - // // mainAxisSize: MainAxisSize.min, - // children: [ - // Image.asset( - // "assets/miscellaneous/image_1.png", - // height: 15, - // width: 15, - // ), - // - // const SizedBox(width: 10), - // ((data['status'] == 'Policy Created') || - // (data['status'] == 'Quotation Accepted')) - // ? const Text('View Quotation') - // : const Text('Create Quotation'), - // ], - // ), - // ), - // ), - // ), - // - // if (data['status'] == 'Quotation Accepted' || - // data['status'] == 'Policy Created') - // Material( - // color: Colors.transparent, - // child: InkWell( - // onTap: () async { - // Navigator.pop(context); - // - // final prefs = await SharedPreferences.getInstance(); - // - // // ✅ Remove old value (if any) - // await prefs.remove('enqStaffDataId'); - // - // // ✅ Save the new id - // await prefs.setString('enqStaffDataId', id.toString()); - // - // // ✅ Update provider too - // - // ref.read(quotationStaffIdProvider.notifier).state = id; - // context.go(AppRoutes.policy); - // }, - // hoverColor: Color(0xFFE3F1F0), - // splashColor: Color(0xFFE3F1F0), - // borderRadius: BorderRadius.circular(6), - // child: Padding( - // padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4), - // child: Row( - // // mainAxisSize: MainAxisSize.min, - // children: [ - // Image.asset( - // "assets/miscellaneous/image_2.png", - // height: 15, - // width: 15, - // ), - // - // const SizedBox(width: 10), - // data['status'] == 'Policy Created' - // ? Text('View Policy') - // : Text('Create Policy'), - // ], - // ), - // ), - // ), - // ), - // ]; - // } + void handleEdit(Map item) { + print("handleEdit"); + final id = int.tryParse(item['id'].toString()) ?? 0; + idPrimary = item['id']; + + print("handleEdit - $idPrimary"); + setState(() { + selectedId = idPrimary; + isEditingRow[id] = true; + }); + for (final field in tabHeader) { + // Only set if controller exists + if (controllers.containsKey(field)) { + controllers[field]!.text = item[field] != null + ? item[field].toString() + : ''; // fallback to empty string + } + } + + //dropdowns or IDs + selectedVehicleType = item['vehicle_type_id']; + selectedAgent = item['agent_id']; + managerId = item['manager_id']; + controllers["regNo"]?.text = item['reg_no']; + controllers["remarks"]?.text = item['remarks']; + + setState(() {}); + + // setState(() { + // isEditingRow[id] = true; + // rowControllers[id] = { + // 'agent_name': TextEditingController(text: item['agent_name'] ?? ''), + // 'insurer_name': TextEditingController(text: item['insurer_name'] ?? ''), + // 'reg_no': TextEditingController(text: item['reg_no'] ?? ''), + // 'premium_amount': TextEditingController( + // text: item['premium_amount'] ?? '', + // ), + // 'payment_mode': TextEditingController(text: item['payment_mode'] ?? ''), + // }; + // }); + } + + void handleSave(int id) { + // final controllers = rowControllers[id]; + // if (controllers == null) return; + if (!_validateRequiredFields()) return; + setState(() { + // final index = getStaffData.indexWhere( + // (e) => e['id'].toString() == id.toString(), + // ); + // if (index != -1) { + // getStaffData[index] = { + // ...getStaffData[index], + // 'agent_name': controllers['agent_name']?.text, + // 'insurer_name': controllers['insurer_name']?.text, + // 'reg_no': controllers['reg_no']?.text, + // 'premium_amount': controllers['premium_amount']?.text, + // 'payment_mode': controllers['payment_mode']?.text, + // }; + // } + // + // if (index != -1) { + // print("EDITField - ${getStaffData[index]}"); + // getStaffData[index] = { + // ...getStaffData[index], + // 'agent_id': selectedAgent, + // "name": controllers["name"]?.text, + // "mobile": controllers["mobile"]?.text, + // "email": controllers["email"]?.text, + // "reg_no": controllers["regNo"]?.text, + // "vehicle_type_id": selectedVehicleType, + // }; + // } + + isEditingRow[id] = false; + rowControllers.remove(id); + }); + handleEnqTabSave(); + } + + void handleCancel(int id) { + setState(() { + // Check if this is a new unsaved row + final row = getStaffData.firstWhere( + (item) => item['id'].toString() == id.toString(), + orElse: () => {}, + ); + + // If policy_number is empty, it's a new row - remove it + if (row.isNotEmpty && + (row['policy_number'] == null || + row['policy_number'] == '' || + row['policy_number'] == '-')) { + getStaffData.removeWhere( + (item) => item['id'].toString() == id.toString(), + ); + originalData.removeWhere( + (item) => item['id'].toString() == id.toString(), + ); + filteredData.removeWhere( + (item) => item['id'].toString() == id.toString(), + ); + rowControllers.remove(id); + } + + // Turn off editing mode + isEditingRow.remove(id); + + // Clear idPrimary if it matches + if (idPrimary.toString() == id.toString()) { + idPrimary = null; + } + }); + } + + //-------------------------------Assign Logics staff start ---------------------------// + + Widget buildSelectStaffMem(ctx) { + Map? selectedVehicle; + try { + selectedVehicle = filteredStaffDataEnqAsgn.firstWhere( + (item) => item['id'].toString() == selectedStaff, + ); + } catch (e) { + selectedVehicle = null; // ✅ fallback + } + + return SizedBox( + height: 35, + width: MediaQuery.of(context).size.width * 0.08, + child: DropdownSearch>( + key: dropDownSelectStaffKey, + // selectedItem: selectedVehicle.isNotEmpty ? selectedVehicle : null, + selectedItem: selectedVehicle, + items: (filter, infiniteScrollProps) { + return filteredStaffDataEnqAsgn; + }, + + itemAsString: (val) => val['name'].toString(), + compareFn: (item, selectedItem) => + item['id'] == selectedItem['id'], // ✅ compare by id + validator: (val) { + if (val == null) { + return "Required"; // ✅ error message + } + return null; + }, + decoratorProps: DropDownDecoratorProps( + decoration: + AppInputDecorations.dropdownDecoration( + label: "Select Staff Member", + ).copyWith( + filled: true, + fillColor: Colors.white, // 👈 makes the dropdown input white + isDense: true, + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(5), + borderSide: const BorderSide(color: Colors.black, width: 0.1), + ), + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(5), + borderSide: const BorderSide(color: Colors.black, width: 0.1), + ), + contentPadding: EdgeInsets.symmetric( + horizontal: 2, + vertical: 6, + ), + ), + ), + dropdownBuilder: (context, selectedItem) => Align( + alignment: Alignment.centerLeft, + child: Text( + selectedItem != null ? selectedItem['name'].toString() : "", + style: GoogleFonts.poppins(fontSize: 11, color: Colors.black), + overflow: TextOverflow.ellipsis, + maxLines: 1, + softWrap: false, + ), + ), + popupProps: PopupProps.menu( + fit: FlexFit.loose, + constraints: BoxConstraints(maxHeight: 250), + menuProps: MenuProps( + backgroundColor: + Colors.white, // 👈 sets dropdown background to white + ), + showSearchBox: true, + searchFieldProps: TextFieldProps( + decoration: InputDecoration( + filled: true, + fillColor: Colors.white, + hintText: "Search Staff ...", + hintStyle: GoogleFonts.inter(fontSize: 12, color: Colors.black), + enabledBorder: OutlineInputBorder( + borderSide: BorderSide(color: Colors.white), // 👈 Normal border + ), + focusedBorder: OutlineInputBorder( + borderSide: BorderSide( + color: Colors.white, + width: 1.5, + ), // 👈 Focused border + ), + ), + ), + // constraints: BoxConstraints(), + itemBuilder: (context, item, isDisabled, isSelected) { + return Container( + // color: isSelected ? Colors.blue.withOpacity(0.1) : null, + padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 3), + child: Text( + item['name'].toString(), + style: GoogleFonts.inter(fontSize: 12, color: Colors.black), + ), + ); + }, + ), + + onChanged: (val) { + if (val != null) { + print("Selected Staff : ${val['name']}"); + print("Id: ${val['id']}"); + selectedStaffName = val['name']; + selectedStaff = val['id']; + // controllers['agentId']?.text = val['agent_code']; + // agentId = agent['id']; + } + }, + ), + ); + } + + Widget buildInsurer(BuildContext context) { + Map? selectedInsurerd = filteredInsurersData.firstWhere( + (item) => item['id'].toString() == selectedInsurer, + orElse: () => {}, + ); + return Container( + height: 35, + width: MediaQuery.of(context).size.width * 0.08, + child: DropdownSearch>( + key: dropDownKeyInsurerEnqAsgn, + selectedItem: selectedInsurerd.isNotEmpty ? selectedInsurerd : null, + items: (filter, infiniteScrollProps) { + return filteredInsurersData; + }, + + itemAsString: (val) => val['name'].toString(), // what to show + compareFn: (item, selectedItem) => + item['id'] == selectedItem['id'], // ✅ compare by id + validator: (val) { + if (val == null) { + return "Required"; // ✅ error message + } + return null; + }, + + dropdownBuilder: (context, selectedItem) => Align( + alignment: Alignment.centerLeft, + child: Text( + selectedItem != null ? selectedItem['name'].toString() : "", + style: GoogleFonts.poppins(fontSize: 11, color: Colors.black), + overflow: TextOverflow.ellipsis, + maxLines: 1, + softWrap: false, + ), + ), + decoratorProps: DropDownDecoratorProps( + decoration: + AppInputDecorations.dropdownDecoration( + label: "Select Insurer", + ).copyWith( + filled: true, + fillColor: Colors.white, // 👈 makes the dropdown input white + isDense: true, + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(5), + borderSide: const BorderSide(color: Colors.black, width: 0.1), + ), + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(5), + borderSide: const BorderSide(color: Colors.black, width: 0.1), + ), + contentPadding: EdgeInsets.symmetric( + horizontal: 2, + vertical: 6, + ), + ), + ), + + popupProps: PopupProps.menu( + fit: FlexFit.loose, + constraints: BoxConstraints(maxHeight: 250), + menuProps: MenuProps( + backgroundColor: + Colors.white, // 👈 sets dropdown background to white + ), + showSearchBox: true, + searchFieldProps: TextFieldProps( + decoration: InputDecoration( + filled: true, + fillColor: Colors.white, + hintText: "Search Insurer...", + hintStyle: GoogleFonts.inter(fontSize: 12, color: Colors.black), + enabledBorder: OutlineInputBorder( + borderSide: BorderSide(color: Colors.white), // 👈 Normal border + ), + focusedBorder: OutlineInputBorder( + borderSide: BorderSide( + color: Colors.white, + width: 1.5, + ), // 👈 Focused border + ), + ), + ), + itemBuilder: (context, item, isDisabled, isSelected) { + return Container( + // color: isSelected ? Colors.blue.withOpacity(0.1) : null, + padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 3), + child: Text( + item['name'].toString(), + style: GoogleFonts.inter(fontSize: 12, color: Colors.black), + ), + ); + }, + // constraints: BoxConstraints(), + ), + + onChanged: (val) { + if (val != null) { + print("Selected Insurer : ${val['name']}"); + print("Id: ${val['id']}"); + selectedInsurer = val['id']; + // controllers['agentId']?.text = val['agent_code']; + // agentId = agent['id']; + } + }, + ), + ); + } + + Widget buildBroker(BuildContext context) { + Map? selectedBrokers = filteredBrokerData.firstWhere( + (item) => item['id'].toString() == selectedBroker, + orElse: () => {}, + ); + return SizedBox( + height: 35, + width: MediaQuery.of(context).size.width * 0.07, + child: DropdownSearch>( + key: dropDownKeyBroker, + selectedItem: selectedBrokers.isNotEmpty ? selectedBrokers : null, + items: (filter, infiniteScrollProps) { + return filteredBrokerData; + }, + + itemAsString: (val) => val['name'].toString(), // what to show + compareFn: (item, selectedItem) => + item['id'] == selectedItem['id'], // ✅ compare by id + validator: (val) { + if (val == null) { + return "Required"; // ✅ error message + } + return null; + }, + dropdownBuilder: (context, selectedItem) => Align( + alignment: Alignment.centerLeft, + child: Text( + selectedItem != null ? selectedItem['name'].toString() : "", + style: GoogleFonts.poppins(fontSize: 11, color: Colors.black), + overflow: TextOverflow.ellipsis, + maxLines: 1, + softWrap: false, + ), + ), + decoratorProps: DropDownDecoratorProps( + decoration: + AppInputDecorations.dropdownDecoration( + label: "Select Broker", + ).copyWith( + filled: true, + fillColor: Colors.white, // 👈 makes the dropdown input white + isDense: true, + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(5), + borderSide: const BorderSide(color: Colors.black, width: 0.1), + ), + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(5), + borderSide: const BorderSide(color: Colors.black, width: 0.1), + ), + contentPadding: EdgeInsets.symmetric( + horizontal: 2, + vertical: 6, + ), + ), + ), + + popupProps: PopupProps.menu( + fit: FlexFit.loose, + constraints: BoxConstraints(maxHeight: 250), + menuProps: MenuProps( + backgroundColor: + Colors.white, // 👈 sets dropdown background to white + ), + showSearchBox: true, + searchFieldProps: TextFieldProps( + decoration: InputDecoration( + filled: true, + fillColor: Colors.white, + hintText: "Search Broker...", + hintStyle: GoogleFonts.inter(fontSize: 12, color: Colors.black), + enabledBorder: OutlineInputBorder( + borderSide: BorderSide(color: Colors.white), // 👈 Normal border + ), + focusedBorder: OutlineInputBorder( + borderSide: BorderSide( + color: Colors.white, + width: 1.5, + ), // 👈 Focused border + ), + ), + ), + + itemBuilder: (context, item, isDisabled, isSelected) { + return Container( + // color: isSelected ? Colors.blue.withOpacity(0.1) : null, + padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 3), + child: Text( + item['name'].toString(), + style: GoogleFonts.inter(fontSize: 12, color: Colors.black), + ), + ); + }, + // constraints: BoxConstraints(), + ), + + onChanged: (val) { + if (val != null) { + print("Selected Broker : ${val['name']}"); + print("Id: ${val['id']}"); + selectedBroker = val['id']; + // controllers['agentId']?.text = val['agent_code']; + // agentId = agent['id']; + } + }, + ), + ); + } + + // -------------------------------Assign Logics staff end---------------------------// + + //---------------------- Enquiry TAB Logins Starts -------------------------------- // + + Map dataEnquiryTabDetails() { + final data = { + "agent_id": ((roleId == 'handler') || (roleId == 'manager')) + ? selectedAgent + : userId, + "name": controllers["name"]?.text, + "mobile": controllers["mobile"]?.text, + "email": controllers["email"]?.text, + "reg_no": controllers["regNo"]?.text, + "vehicle_type_id": selectedVehicleType, + "is_data_created_by_handler": roleId == 'handler' ? '1' : '0', + "is_data_created_by_manager": roleId == 'manager' ? '1' : '0', + // "insurer_id": selectedInsurer, + // "rc_file_name": "rc_doc.pdf", + // "id_proof_file_name": "id_proof.pdf", + // "previous_policy_file_name": "previous_policy.pdf", + "remarks": controllers["remarks"]?.text, + "manager_id": managerId, + "created_by": userId, + }; + + return data; + } + + Future getVehicleType() async { + print('getClaimList called'); + setState(() { + isLoading = true; + }); + + try { + final response = await apiService.fetchMasterDropDown('vehicleType'); + + if (response['status'] == 200) { + print('getVehicleTypeData - ${response['data']}'); + setState(() { + getVehicleTypeData = List>.from( + response['data'], + ); + print('API Data - $getVehicleTypeData'); + + filteredVechicleData = List.from(getVehicleTypeData); + print('originalData - $filteredVechicleData'); + }); + } else { + getVehicleTypeData = []; + filteredVechicleData = []; + } + } catch (e) { + print('Exception occurred: $e'); + } finally { + setState(() { + isLoading = false; + }); + } + } + + Future getAgentList(id) async { + print('getAgentListData called'); + setState(() { + isLoading = true; + }); + + try { + final response = await apiService.fetchAgentNameDropDown(id); + print('getAgentListData called response'); + print('get Agent- ${response['data']}'); + if (response['status'] == 'success') { + print('get Agent- ${response['data']}'); + setState(() { + getAgentListData = List>.from(response['data']); + print('API Data - $getAgentListData'); + + filteredAgentData = List.from(getAgentListData); + print('originalAgentData - $filteredAgentData'); + }); + } else { + getAgentListData = []; + filteredAgentData = []; + } + } catch (e) { + print('Exception occurred: $e'); + } finally { + setState(() { + isLoading = false; + }); + } + } + + Widget buildAgentName(BuildContext context) { + Map? selectedAgntName = filteredAgentData.firstWhere( + (item) => item['id'].toString() == selectedAgent, + orElse: () => {}, + ); + return SizedBox( + width: MediaQuery.of(context).size.width * 0.08, + height: 35, + child: DropdownSearch>( + key: dropDownKeyAgent, + selectedItem: selectedAgntName.isNotEmpty ? selectedAgntName : null, + items: (filter, infiniteScrollProps) { + return filteredAgentData; + }, + + itemAsString: (val) => val['name'].toString(), // what to show + compareFn: (item, selectedItem) => + item['id'] == selectedItem['id'], // ✅ compare by id + validator: (val) { + if (val == null) { + return "Required"; // ✅ error message + } + return null; + }, + dropdownBuilder: (context, selectedItem) => Align( + alignment: Alignment.centerLeft, + child: Text( + selectedItem != null + ? selectedItem['name'].toString() + : "", // ✅ FIXED + style: GoogleFonts.poppins(fontSize: 11, color: Colors.black), + overflow: TextOverflow.ellipsis, + maxLines: 1, + softWrap: false, + ), + ), + decoratorProps: DropDownDecoratorProps( + decoration: + AppInputDecorations.dropdownDecoration( + label: "Select Partner", + ).copyWith( + filled: true, + fillColor: Colors.white, // 👈 makes the dropdown input white + isDense: true, + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(5), + borderSide: const BorderSide(color: Colors.black, width: 0.1), + ), + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(5), + borderSide: const BorderSide(color: Colors.black, width: 0.1), + ), + contentPadding: EdgeInsets.symmetric( + horizontal: 1, + // vertical: 6, + ), + ), + ), + + popupProps: PopupProps.menu( + fit: FlexFit.loose, + constraints: BoxConstraints(maxHeight: 250), + menuProps: MenuProps( + backgroundColor: + Colors.white, // 👈 sets dropdown background to white + ), + showSearchBox: true, + + searchFieldProps: TextFieldProps( + decoration: InputDecoration( + filled: true, + fillColor: Colors.white, + hintText: "Search Partner...", + hintStyle: GoogleFonts.inter(fontSize: 12, color: Colors.black), + enabledBorder: OutlineInputBorder( + borderSide: BorderSide(color: Colors.white), // 👈 Normal border + ), + focusedBorder: OutlineInputBorder( + borderSide: BorderSide( + color: Colors.white, + width: 1.5, + ), // 👈 Focused border + ), + ), + ), + + // constraints: BoxConstraints(), + itemBuilder: (context, item, isDisabled, isSelected) { + return Container( + // color: isSelected ? Colors.blue.withOpacity(0.1) : null, + padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 3), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Text( + item['name'].toString(), + style: GoogleFonts.inter(fontSize: 12, color: Colors.black), + ), + Text( + item['agent_code'].toString(), + style: GoogleFonts.inter(fontSize: 11, color: Colors.grey), + ), + ], + ), + ); + }, + ), + + onChanged: (val) { + if (val != null) { + print("Selected Partner : ${val['name']}"); + print("Id: ${val['id']}"); + selectedAgent = val['id']; + // controllers['agentId']?.text = val['agent_code']; + // agentId = agent['id']; + } + }, + ), + ); + } + + Widget buildVehicleNumber(BuildContext context) { + return ThemedFormInlineField( + controller: controllers['regNo']!, + borderColor: Colors.black, + isdense: true, + inputFormatters: [ + UpperCaseTextFormatter(), // 👈 custom formatter for uppercase + FilteringTextInputFormatter.allow(RegExp(r'[A-Za-z0-9- ]')), + ], + validator: (value) => Validators.requiredVechileNum(value, "regNo"), + widthNone: true, + ); + } + + Widget buildInsuredName(BuildContext context) { + return ThemedFormInlineField( + controller: controllers['name']!, + // errorText: fieldErrors['name'], + borderColor: Colors.black, + isdense: true, + validator: (value) => Validators.requiredField(value, "name"), + widthNone: true, + ); + } + + Widget buildVehicleType(BuildContext context) { + // Find the matching map from your list + + Map? selectedVehicle = filteredVechicleData.firstWhere( + (item) => item['id'].toString() == selectedVehicleType, + orElse: () => {}, + ); + + return SizedBox( + width: MediaQuery.of(context).size.width * 0.07, + height: 35, + child: DropdownSearch>( + key: dropDownKey, + selectedItem: selectedVehicle.isNotEmpty ? selectedVehicle : null, + items: (filter, infiniteScrollProps) { + return filteredVechicleData; + }, + validator: (val) { + if (val == null) { + return "Required"; // ✅ error message + } + return null; + }, + + itemAsString: (val) => val['vehicle_type'].toString(), + compareFn: (item, selectedItem) => + item['id'] == selectedItem['id'], // ✅ compare by id + + dropdownBuilder: (context, selectedItem) => Align( + alignment: Alignment.centerLeft, + child: Text( + selectedItem != null + ? selectedItem['vehicle_type'].toString() + : "", // ✅ FIXED + style: GoogleFonts.poppins(fontSize: 11, color: Colors.black), + overflow: TextOverflow.ellipsis, + maxLines: 1, + softWrap: false, + ), + ), + decoratorProps: DropDownDecoratorProps( + decoration: + AppInputDecorations.dropdownDecoration( + label: "Select Vehicle Type", + ).copyWith( + filled: true, + fillColor: Colors.white, // 👈 makes the dropdown input white + isDense: true, + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(5), + borderSide: const BorderSide(color: Colors.black, width: 0.1), + ), + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(5), + borderSide: const BorderSide(color: Colors.black, width: 0.1), + ), + contentPadding: EdgeInsets.symmetric( + horizontal: 8, + vertical: 6, + ), + ), + ), + + popupProps: PopupProps.menu( + fit: FlexFit.loose, + constraints: BoxConstraints(maxHeight: 200), + menuProps: MenuProps( + backgroundColor: + Colors.white, // 👈 sets dropdown background to white + ), + showSearchBox: true, + searchFieldProps: TextFieldProps( + decoration: InputDecoration( + filled: true, + fillColor: Colors.white, + hintText: "Search Vehicle Type...", + hintStyle: GoogleFonts.inter(fontSize: 12, color: Colors.black), + enabledBorder: OutlineInputBorder( + borderSide: BorderSide(color: Colors.white), // 👈 Normal border + ), + focusedBorder: OutlineInputBorder( + borderSide: BorderSide( + color: Colors.white, + width: 1.5, + ), // 👈 Focused border + ), + ), + ), + + // constraints: BoxConstraints(), + itemBuilder: (context, item, isDisabled, isSelected) { + return Container( + // color: isSelected ? Colors.blue.withOpacity(0.1) : null, + padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 3), + child: Text( + item['vehicle_type'].toString(), + style: GoogleFonts.inter(fontSize: 12, color: Colors.black), + ), + ); + }, + ), + + onChanged: (val) { + if (val != null) { + print("Selected vehicle_type : ${val['vehicle_type']}"); + print("Id: ${val['id']}"); + selectedVehicleType = val['id']; + // controllers['agentId']?.text = val['agent_code']; + // agentId = agent['id']; + } + }, + ), + ); + } + + Widget buildEmail(BuildContext context) { + return ThemedFormInlineField( + controller: controllers['email']!, + borderColor: Colors.black, + isdense: true, + validator: (value) => Validators.nonReqemail(value, "email"), + inputFormatters: [ + FilteringTextInputFormatter.allow(RegExp(r'[a-zA-Z0-9_@.]')), + ], + txtwidth: ResponsiveLayout.isMobile(context) + ? null + : MediaQuery.of(context).size.width * 0.26, + ); + } + + Widget buildPhNumber(BuildContext context) { + return ThemedFormInlineField( + controller: controllers['mobile']!, + borderColor: Colors.black, + isdense: true, + validator: (value) => Validators.nonReqphone(value, "phone"), + inputFormatters: [FilteringTextInputFormatter.allow(RegExp(r'[ 0-9]'))], + txtwidth: ResponsiveLayout.isMobile(context) + ? null + : MediaQuery.of(context).size.width * 0.26, + ); + } + + Widget buildRemarks(BuildContext context) { + return ThemedFormField( + // maxLength: 500, + enableBorderWidth: 0.1, + controller: controllers['remarks']!, + borderColor: Colors.black, + verticalPad: 8, + horizonalPad: 10, + isdense: true, + // keyboardType: TextInputType.multiline, + txtwidth: ResponsiveLayout.isMobile(context) + ? null + : MediaQuery.of(context).size.width * 0.1, + ); + } + + Widget buildResponsiveUploadField({ + required String label, + required String? hintText, + required double? width, + required void Function(String fileName, dynamic file) onFileSelected, + bool showDownload = false, + VoidCallback? onRemove, + VoidCallback? onDownload, + }) { + final isMobile = ResponsiveLayout.isMobile(context); + + final uploadWidget = ThemedUploadField( + hintText: hintText ?? "Upload Document", + txtwidth: width, + padVertical: 6, + padHorizontal: 8, + + backgroundColor: Color(0xffD9EBE8), + txtName: label, + isTxtBtnCase: true, + onFileSelected: onFileSelected, + ); + + return Column( + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [uploadWidget], + ), + ], + ); + } + + Widget buildUploadRCDocument(BuildContext context) { + return buildResponsiveUploadField( + label: "RC", + width: 60, + hintText: selectedRCFile, + onFileSelected: (fileName, file) { + setState(() { + docUploadedRCFile = file; + selectedRCFileName = fileName; + }); + ToastHelper.showSuccessToast(context, 'File Uploaded Successfully'); + }, + showDownload: docUploadedRCFile != null || rcFileUrlFromApi != null, + onRemove: () { + setState(() { + docUploadedRCFile = null; + rcFileUrlFromApi = null; + selectedRCFileName = null; + }); + }, + onDownload: () => apiService.downloadFile( + // apiUrl: 'api/agent/downloadAgentCertificateFile?agent_id=$selectedId', + apiUrl: + 'api/enquiry/downloadEnquiryFile?enquiry_id=$selectedId&file_type=rc', + + apiId: selectedId.toString(), + + localFile: docUploadedRCFile, + fileName: 'RC', + ), + ); + } + + Widget buildUploadIDDocument(BuildContext context) { + return buildResponsiveUploadField( + label: "ID Proof", + width: 80, + hintText: selectedIdProof, + onFileSelected: (fileName, file) { + setState(() { + docUploadedIDProof = file; + // selectedFileNames = fileName; + }); + + ToastHelper.showSuccessToast(context, 'File Uploaded Successfully'); + }, + showDownload: docUploadedIDProof != null || idProofFileUrlFromApi != null, + onRemove: () { + setState(() { + docUploadedIDProof = null; + idProofFileUrlFromApi = null; + // selectedFileNames = null; + }); + }, + onDownload: () => apiService.downloadFile( + // apiUrl: 'api/agent/downloadAgentCertificateFile?agent_id=$selectedId', + apiUrl: + 'api/enquiry/downloadEnquiryFile?enquiry_id=$selectedId&file_type=id_proof', + + apiId: selectedId.toString(), + localFile: docUploadedIDProof, + fileName: 'Id_Proof', + ), + ); + } + + Widget buildUploadPolicyDocument(BuildContext context) { + return buildResponsiveUploadField( + width: 100, + label: "PrevPolicy", + hintText: selectedPrevPolicy, + onFileSelected: (fileName, file) { + setState(() { + docUploadedPrevPolicy = file; + // selectedFileNames = fileName; + }); + ToastHelper.showSuccessToast(context, 'File Uploaded Successfully'); + }, + showDownload: + docUploadedPrevPolicy != null || prevPolicyFileUrlFromApi != null, + onRemove: () { + setState(() { + docUploadedPrevPolicy = null; + prevPolicyFileUrlFromApi = null; + // selectedFileNames = null; + }); + }, + onDownload: () => apiService.downloadFile( + // apiUrl: 'api/agent/downloadAgentCertificateFile?agent_id=$selectedId', + apiUrl: + 'api/enquiry/downloadEnquiryFile?enquiry_id=$selectedId&file_type=previous_policy', + + apiId: selectedId.toString(), + localFile: docUploadedPrevPolicy, + fileName: 'Previous_Policy', + ), + ); + } + + Widget buildDocRC(BuildContext context, selectedEnquiryId) { + return InkWell( + onTap: () { + print("Upload $selectedEnquiryId"); + final selectedId = selectedEnquiryId; + // final path = + // 'api/agent/downloadAgentIncentiveFile?id=$selectedId'; + final path = + 'api/enquiry/downloadEnquiryFile?enquiry_id=$selectedId&file_type=rc'; + print("Uploadpath $path"); + apiService.getPdfDownload(path, selectedId); + }, + child: Container( + padding: const EdgeInsets.symmetric(vertical: 4, horizontal: 6), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(5), + color: const Color(0xFF425B5B), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Tooltip( + message: 'Download RC', + child: Text( + "RC", + style: GoogleFonts.inter( + fontSize: 12, + fontWeight: FontWeight.w500, + color: Colors.white, + ), + ), + ), + ], + ), + ), + ); + } + + Widget buildDocIdProof(BuildContext context, selectedEnquiryId) { + return InkWell( + onTap: () { + print("Upload $selectedEnquiryId"); + final selectedId = selectedEnquiryId; + // final path = + // 'api/agent/downloadAgentIncentiveFile?id=$selectedId'; + final path = + 'api/enquiry/downloadEnquiryFile?enquiry_id=$selectedId&file_type=id_proof'; + print("Uploadpath $path"); + apiService.getPdfDownload(path, selectedId); + }, + child: Container( + padding: const EdgeInsets.symmetric(vertical: 4, horizontal: 6), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(5), + color: const Color(0xFF425B5B), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Tooltip( + message: 'Download ID Proof', + child: Text( + "ID Proof", + style: GoogleFonts.inter( + fontSize: 12, + fontWeight: FontWeight.w500, + color: Colors.white, + ), + ), + ), + ], + ), + ), + ); + } + + Widget buildDocPrevPolicy(BuildContext context, selectedEnquiryId) { + return InkWell( + onTap: () { + print("QD597 Upload $selectedEnquiryId"); + final selectedId = selectedEnquiryId; + // final path = + // 'api/agent/downloadAgentIncentiveFile?id=$selectedId'; + final path = + 'api/enquiry/downloadEnquiryFile?enquiry_id=$selectedId&file_type=previous_policy'; + + print("Uploadpath $path"); + apiService.getPdfDownload(path, selectedId); + }, + child: Container( + padding: const EdgeInsets.symmetric(vertical: 4, horizontal: 6), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(5), + color: const Color(0xFF425B5B), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Tooltip( + message: 'Download Previous Policy', + child: Text( + "Prev Policy", + style: GoogleFonts.inter( + fontSize: 12, + fontWeight: FontWeight.w500, + color: Colors.white, + ), + ), + ), + ], + ), + ), + ); + } + + // Update validation method to set errors + bool _validateRequiredFields() { + setState(() { + fieldErrors.clear(); // Clear previous errors + + // Validate Partner/Agent + if (selectedAgent == null || selectedAgent == '') { + fieldErrors['agent'] = 'Partner is required'; + } + + // Validate Insured Name + if (controllers['name']?.text.trim().isEmpty ?? true) { + fieldErrors['name'] = 'Insured Name is required'; + } + + // Validate Vehicle Number + if (controllers['regNo']?.text.trim().isEmpty ?? true) { + fieldErrors['regNo'] = 'Vehicle Number is required'; + } + + // Validate Vehicle Type + if (selectedVehicleType == null || selectedVehicleType == '') { + fieldErrors['vehicleType'] = 'Vehicle Type is required'; + } + + // Validate email format if provided + final email = controllers['email']?.text.trim() ?? ''; + if (email.isNotEmpty) { + final emailRegex = RegExp(r'^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$'); + if (!emailRegex.hasMatch(email)) { + fieldErrors['email'] = 'Invalid email format'; + } + } + + // Validate mobile number if provided + final mobile = controllers['mobile']?.text.trim() ?? ''; + if (mobile.isNotEmpty) { + final cleanMobile = mobile.replaceAll(' ', ''); + if (cleanMobile.length < 10) { + fieldErrors['mobile'] = 'Must be at least 10 digits'; + } + } + }); + + return true; + + return fieldErrors.isEmpty; + } + + Future handleEnqTabSave() async { + if (!_validateRequiredFields()) return; + + // setState(() { + // isSaving = true; // start saving + // }); + + final dataSet = dataDetails(); + + try { + await createUserData(dataSet); // API call + // ToastHelper.showInfoToast(context, "Enquiry saved successfully"); + } catch (e) { + showDialog( + context: context, + builder: (_) => AlertDialog( + title: const Text('Error'), + content: Text(e.toString()), + actions: [ + TextButton( + onPressed: () => Navigator.of(context).pop(), + child: const Text('OK'), + ), + ], + ), + ); + } finally { + // setState(() => isSaving = false); // re-enable button after save + } + } + + Future attachFiles(http.MultipartRequest request) async { + Future addFileOrKeepName( + PlatformFile? file, + String? apiFileName, + String fieldName, + ) async { + if (file != null) { + // User uploaded a new file → send as multipart + if (file.bytes != null) { + request.files.add( + http.MultipartFile.fromBytes( + fieldName, + file.bytes!, + filename: file.name, + ), + ); + } else if (file.path != null) { + request.files.add( + await http.MultipartFile.fromPath( + fieldName, + file.path!, + filename: file.name, + ), + ); + } + print("📎 Attached new file → $fieldName"); + } else if (apiFileName != null && apiFileName.isNotEmpty) { + // No new upload → tell backend to keep old file + request.fields[fieldName] = apiFileName; + print("🔗 Kept old file → $fieldName = $apiFileName"); + } else { + // Nothing at all + request.fields[fieldName] = ""; + } + } + + await addFileOrKeepName( + docUploadedRCFile, + rcFileUrlFromApi, + 'rc_file_name', + ); + await addFileOrKeepName( + docUploadedIDProof, + idProofFileUrlFromApi, + 'id_proof_file_name', + ); + await addFileOrKeepName( + docUploadedPrevPolicy, + prevPolicyFileUrlFromApi, + 'previous_policy_file_name', + ); + } + + Future createUserData(Map userData) async { + final bool isNewRow = + idPrimary != null && + int.tryParse(idPrimary.toString()) != null && + int.parse(idPrimary.toString()) > 1000000000000; + + final bool isUpdating = + idPrimary != null && idPrimary != 'null' && !isNewRow; + + print('isNewRow: $isNewRow, isUpdating: $isUpdating'); + + // final bool isUpdating = idPrimary != 'null'; + final id = idPrimary; + print('UPDId- $id'); + + // final bool isUpdating = false; + final uri = Uri.parse( + isUpdating + ? '${Env.apiUrl}enquiry/updateEnquiry' + : '${Env.apiUrl}enquiry/createEnquiry', + ); + if (_token == null) { + throw Exception('Token not found. Please log in.'); + } + + // Use MultipartRequest (POST only) + final request = http.MultipartRequest('POST', uri); + request.headers['Authorization'] = 'Bearer $_token'; + request.headers['app-signature'] = Env.App_Signature; + + // If updating, spoof the method Laravel-style + if (isUpdating) { + request.fields['id'] = id!; + request.fields['updated_by'] = userId!.toString(); + } else { + request.fields['created_by'] = userId!.toString(); + } + + print("USerDAta - $userData"); + + // userData.forEach((key, value) { + // request.fields[key] = value.toString(); + // print("✅ Encoded travel_details2: ${request.fields[key]}"); + // }); + await attachFiles(request); + userData.forEach((key, value) { + if (key != 'rc_file_name' && key != 'id_proof_file_name') { + request.fields[key] = value.toString(); + print("✅ Encoded $key: ${request.fields[key]}"); + } else { + print('Something Missing..'); + } + }); + + // attach files + + // request.fields['agent_id'] = selectedId.toString(); + + print(" Sending request with fields: ${request.fields}"); + + try { + final streamedResponse = await request.send(); + final response = await http.Response.fromStream(streamedResponse); + print("Response status: ${response.statusCode}"); + print("Response body: ${response.body}"); + + if (response.statusCode == 200 || response.statusCode == 201) { + // dispose(); + print("✅ Enquiry submitted successfully!"); + ToastHelper.showSuccessToast(context, 'Saved Enquiry'); + print("Response: ${response.body}"); + // Clear the temporary ID + idPrimary = null; + refrshfilterDateRange(); + // getStaffList(managerId, roleId); + // setState(() => isSaving = false); + } else if (response.statusCode == 403) { + await apiService.clearLocalStorageAndRedirect(); + } else { + print("❌ Submission failed. Status: ${response.statusCode}"); + print("Body: ${response.body}"); + + // setState(() => isSaving = false); + showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + title: Text("Enquiry Creation Failed"), + content: Text( + "There was a problem in creating enquiry. Please try again.", + ), + actions: [ + TextButton( + child: Text("OK"), + onPressed: () { + Navigator.of(context).pop(); + }, + ), + ], + ); + }, + ); + } + } catch (e) { + print("🔥 Error submitting user: $e"); + } + } + + //---------------------- Enquiry TAB Logins End -------------------------------- // @override Widget build(BuildContext context) { return MainLayout( @@ -573,57 +1938,58 @@ class EnquiryHandlerState extends ConsumerState { body: Container( // color: Colors.yellow.shade50, width: MediaQuery.of(context).size.width, + padding: EdgeInsets.all(3.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start, children: [ - Container( - height: 30, - width: MediaQuery.of(context).size.width, - child: GestureDetector( - onTap: () { - context.go(AppRoutes.dashboard); - }, - child: Row( - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisAlignment: MainAxisAlignment.start, - children: [ - Tooltip( - message: 'Back', - child: IconButton( - icon: const Icon(Icons.arrow_left_sharp, size: 25), - onPressed: () async { - context.go(AppRoutes.dashboard); - final prefs = await SharedPreferences.getInstance(); - await prefs.remove('dashboardKeyProvider'); - await prefs.remove('dashboardStatusProvider'); - await prefs.remove('dashboardStaffIdProvider'); - }, - splashRadius: 18, - padding: const EdgeInsets.all(4), - hoverColor: Colors.black12, - constraints: const BoxConstraints(), - ), - ), - const SizedBox(width: 5), // spacing between icon and text - Text( - "Enquiries", - style: TextStyle( - fontSize: 18, - fontWeight: FontWeight.w600, - ), - ), - ], - ), - ), - ), - SizedBox(height: 5), + // Container( + // height: 30, + // width: MediaQuery.of(context).size.width, + // child: GestureDetector( + // onTap: () { + // context.go(AppRoutes.dashboard); + // }, + // child: Row( + // crossAxisAlignment: CrossAxisAlignment.center, + // mainAxisAlignment: MainAxisAlignment.start, + // children: [ + // Tooltip( + // message: 'Back', + // child: IconButton( + // icon: const Icon(Icons.arrow_left_sharp, size: 25), + // onPressed: () async { + // context.go(AppRoutes.dashboard); + // final prefs = await SharedPreferences.getInstance(); + // await prefs.remove('dashboardKeyProvider'); + // await prefs.remove('dashboardStatusProvider'); + // await prefs.remove('dashboardStaffIdProvider'); + // }, + // splashRadius: 18, + // padding: const EdgeInsets.all(4), + // hoverColor: Colors.black12, + // constraints: const BoxConstraints(), + // ), + // ), + // const SizedBox(width: 5), // spacing between icon and text + // Text( + // "Enquiries", + // style: TextStyle( + // fontSize: 18, + // fontWeight: FontWeight.w600, + // ), + // ), + // ], + // ), + // ), + // ), + // SizedBox(height: 5), ResponsiveLayout.isMobile(context) ? Container( height: MediaQuery.of(context).size.height * 0.69, child: SingleChildScrollView( child: Padding( - padding: EdgeInsets.all(8), + padding: EdgeInsets.all(2), child: _buildContent(context), ), ), @@ -631,21 +1997,14 @@ class EnquiryHandlerState extends ConsumerState { : Expanded( child: Container( width: MediaQuery.of(context).size.width, - padding: EdgeInsets.all(8.0), + // padding: EdgeInsets.symmetric( + // horizontal: 8.0, + // vertical: 2.0, + // ), child: _buildContent(context), ), ), - // Expanded( - // child: Container( - // // color: Colors.green, - // // color: Colors.green.shade50, - // width: MediaQuery.of(context).size.width, - // - // padding: EdgeInsets.all(8.0), - // child: - // ), - // ), Container( // height: 20, width: MediaQuery.of(context).size.width, @@ -677,49 +2036,89 @@ class EnquiryHandlerState extends ConsumerState { Widget _buildContent(BuildContext context) { return Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ - if (ResponsiveLayout.isMobile(context)) ...[ - Container( - padding: EdgeInsets.all(8.0), - color: Color(0xffD9EBE8), - child: DateFilterRow( - key: ValueKey(SelectedStatus ?? ''), - onFilterStaff: (val) { - print('Selected Filterd STAFF Id - $val'); - SelectedStaffId = val; - }, - selectedStaffId: SelectedStaffId, - role: roleId, - id: userId, - selectedStatusVal: SelectedStatus, - startController: controllers['startDate']!, - endController: controllers['endDate']!, - formKey: _formKey, - isMobile: ResponsiveLayout.isMobile(context), - onStatusChanged: (val) { - SelectedStatus = val; // update parent - }, - - onFilter: () { - // call your filter logic - filterDateRange(); - }, - onRefresh: () { - // call your refresh logic - refrshfilterDateRange(); - }, - ), - ), - ], - + // if (ResponsiveLayout.isMobile(context)) ...[ + // Container( + // padding: EdgeInsets.all(8.0), + // color: Color(0xffD9EBE8), + // child: DateFilterRow( + // key: ValueKey(SelectedStatus ?? ''), + // onFilterStaff: (val) { + // print('Selected Filterd STAFF Id - $val'); + // SelectedStaffId = val; + // }, + // selectedStaffId: SelectedStaffId, + // role: roleId, + // id: userId, + // selectedStatusVal: SelectedStatus, + // startController: controllers['startDate']!, + // endController: controllers['endDate']!, + // formKey: _formKey, + // isMobile: ResponsiveLayout.isMobile(context), + // onStatusChanged: (val) { + // SelectedStatus = val; // update parent + // }, + // + // onFilter: () { + // // call your filter logic + // filterDateRange(); + // }, + // onRefresh: () { + // // call your refresh logic + // refrshfilterDateRange(); + // }, + // ), + // ), + // ], Container( // height: 40, // color: Colors.pink, child: Row( - crossAxisAlignment: CrossAxisAlignment.end, + crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start, children: [ - if (!ResponsiveLayout.isMobile(context)) ...[ + Container( + // height: 30, + // width: MediaQuery.of(context).size.width, + child: GestureDetector( + onTap: () { + context.go(AppRoutes.dashboard); + }, + child: Row( + // crossAxisAlignment: CrossAxisAlignment.center, + // mainAxisAlignment: MainAxisAlignment.start, + children: [ + Tooltip( + message: 'Back', + child: IconButton( + icon: const Icon(Icons.arrow_left_sharp, size: 25), + onPressed: () async { + context.go(AppRoutes.dashboard); + final prefs = await SharedPreferences.getInstance(); + await prefs.remove('dashboardKeyProvider'); + await prefs.remove('dashboardStatusProvider'); + await prefs.remove('dashboardStaffIdProvider'); + }, + splashRadius: 18, + padding: const EdgeInsets.all(4), + hoverColor: Colors.black12, + constraints: const BoxConstraints(), + ), + ), + const SizedBox(width: 5), // spacing between icon and text + Text( + "Enquiries", + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.w600, + ), + ), + ], + ), + ), + ), + if (_showFilterRow) ...[ DateFilterRow( key: ValueKey(SelectedStatus ?? ''), role: roleId, @@ -746,14 +2145,26 @@ class EnquiryHandlerState extends ConsumerState { refrshfilterDateRange(); }, ), - - Spacer(), ], - + IconButton( + icon: Icon( + _showFilterRow + ? Icons.close_fullscreen + : Icons.filter_alt_outlined, + ), + tooltip: _showFilterRow ? 'Hide Filters' : 'Show Filters', + onPressed: () { + setState(() { + _showFilterRow = !_showFilterRow; + }); + }, + ), + Spacer(), ThemedSearchField( hintText: 'Search', backgroundColor: Color(0xFFF6F8F8), onChanged: filterData, + txtHeight: 35, controller: _searchStaffController, txtwidth: ResponsiveLayout.isMobile(context) ? MediaQuery.of(context).size.width * 0.7 @@ -799,12 +2210,72 @@ class EnquiryHandlerState extends ConsumerState { SizedBox(width: 10), GestureDetector( onTap: () async { - final prefs = await SharedPreferences.getInstance(); - - await prefs.remove('enqAgentDataId'); - ref.read(enquiryIdProvider.notifier).state = null; - context.go(AppRoutes.tabEnquiry); + // final prefs = await SharedPreferences.getInstance(); + // + // await prefs.remove('enqAgentDataId'); + // ref.read(enquiryIdProvider.notifier).state = null; + // context.go(AppRoutes.tabEnquiry); // print('Export'); + + // Generate a unique ID for the new row + final newId = DateTime.now().millisecondsSinceEpoch; + + // Clear all existing form controllers + for (String field in tabHeader) { + controllers[field]?.clear(); + } + + // Reset dropdown selections + selectedVehicleType = null; + selectedAgent = null; + selectedInsurer = null; + + // Reset file uploads + docUploadedRCFile = null; + docUploadedIDProof = null; + docUploadedPrevPolicy = null; + selectedRCFile = null; + selectedIdProof = null; + selectedPrevPolicy = null; + + setState(() { + // Create a new empty row data + final newRow = { + 'id': newId.toString(), // Keep as String for consistency + 'created_on': '', + 'agent_name': '', + 'agent_id': '', + 'assigned_to_name': '', + 'insurer_name': '', + 'insured_name': '', + 'reg_no': '', + 'vehicle_type': '', + 'vehicle_type_id': '', + 'email': '', + 'mobile': '', + 'remarks': '', + 'updated_on': '', + 'premium_amount': '', + 'payment_mode': '', + 'policy_number': '', + 'status': '', + }; + + // Insert at the beginning of ALL lists + getStaffData.insert(0, newRow); + originalData.insert(0, newRow); + filteredData.insert(0, newRow); + + // Set to editing mode immediately + isEditingRow[newId] = true; + + // Store the ID as string + idPrimary = newId.toString(); + selectedId = newId.toString(); + + // Reset to first page to see the new row + currentPage = 1; + }); }, child: Container( padding: EdgeInsets.all(8.0), @@ -815,117 +2286,85 @@ class EnquiryHandlerState extends ConsumerState { child: Row( mainAxisSize: MainAxisSize.min, children: [ - Icon(Icons.add, color: Colors.white), + Tooltip( + message: 'Raise Enquiry', + child: Icon(Icons.add, color: Colors.white, size: 15), + ), - if (!ResponsiveLayout.isMobile(context)) ...[ - SizedBox(width: 10), - Text( - 'Raise Enquiry', - style: GoogleFonts.inter( - color: Colors.white, - fontWeight: FontWeight.w600, - fontSize: 14, - ), - ), - ], + // if (!ResponsiveLayout.isMobile(context)) ...[ + // SizedBox(width: 10), + // Text( + // 'Raise Enquiry', + // style: GoogleFonts.inter( + // color: Colors.white, + // fontWeight: FontWeight.w600, + // fontSize: 14, + // ), + // ), + // ], ], ), ), ), - // SizedBox(width: 10), - // GestureDetector( - // onTap: () { - // // print('Export'); - // }, - // child: Container( - // padding: EdgeInsets.all(8.0), - // decoration: BoxDecoration( - // color: Color(0xFF425B5B), - // borderRadius: BorderRadius.circular(8.0), - // ), - // child: Row( - // mainAxisSize: MainAxisSize.min, - // children: [ - // Icon(Icons.add, color: Colors.white), - // - // if (!ResponsiveLayout.isMobile(context)) ...[ - // SizedBox(width: 10), - // - // GestureDetector( - // onTap: () { - // // context.go(AppRoutes.agent / create); - // context.go('/staff/create'); - // }, - // child: Text( - // 'Create New Staff', - // style: TextStyle( - // color: Colors.white, - // fontWeight: FontWeight.w600, - // fontSize: 14, - // ), - // ), - // ), - // ], - // ], - // ), - // ), - // ), ], ), ), - SizedBox(height: 10), - if (!ResponsiveLayout.isMobile(context)) - Container( - decoration: BoxDecoration( - color: Color(0xFFEDF6F5), - borderRadius: BorderRadius.circular(6), - ), - padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 16), - child: const Row( - children: [ - Expanded( - flex: 2, - child: Text('Received Date ', style: _headerStyle), - ), + // SizedBox(height: 10), - Expanded(flex: 2, child: Text('Partner ', style: _headerStyle)), - Expanded( - flex: 2, - child: Text('Assigned To ', style: _headerStyle), - ), - Expanded(flex: 3, child: Text('Insurer ', style: _headerStyle)), - - Expanded( - flex: 2, - child: Text('Vehicle.No ', style: _headerStyle), - ), - - // Expanded( - // flex: 2, - // // child: Padding( - // // padding: EdgeInsets.only(left: 8.0), - // child: Text('Insured Name ', style: _headerStyle), - // // ), - // ), - Expanded( - flex: 2, - child: Text('Assigned Date ', style: _headerStyle), - ), - Expanded(flex: 2, child: Text('Premium ', style: _headerStyle)), - Expanded( - flex: 2, - child: Text('Payment Mode ', style: _headerStyle), - ), - Expanded( - flex: 2, - child: Text('Policy Number ', style: _headerStyle), - ), - - Expanded(flex: 2, child: Text('Status ', style: _headerStyle)), - Expanded(flex: 1, child: Text('Action', style: _headerStyle)), - ], - ), - ), + // Container( + // decoration: BoxDecoration( + // color: const Color(0xFFEDF6F5), + // borderRadius: BorderRadius.circular(6), + // ), + // padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 16), + // child: Row( + // children: const [ + // Expanded(flex: 1, child: Text('S.No', style: _headerStyle)), + // Expanded( + // flex: 2, + // child: Text('Received Date', style: _headerStyle), + // ), + // Expanded(flex: 2, child: Text('Partner *', style: _headerStyle)), + // Expanded( + // flex: 2, + // child: Text('Assigned To *', style: _headerStyle), + // ), + // Expanded(flex: 2, child: Text('Broker *', style: _headerStyle)), + // Expanded(flex: 3, child: Text('Insurer *', style: _headerStyle)), + // Expanded( + // flex: 3, + // child: Text('Insured Name *', style: _headerStyle), + // ), + // Expanded( + // flex: 2, + // child: Text('Vehicle No *', style: _headerStyle), + // ), + // Expanded( + // flex: 2, + // child: Text('Vehicle Type *', style: _headerStyle), + // ), + // Expanded(flex: 2, child: Text('Email', style: _headerStyle)), + // Expanded(flex: 2, child: Text('Mobile', style: _headerStyle)), + // Expanded(flex: 2, child: Text('Documents', style: _headerStyle)), + // Expanded(flex: 2, child: Text('Remarks', style: _headerStyle)), + // Expanded( + // flex: 2, + // child: Text('Assigned Date', style: _headerStyle), + // ), + // Expanded(flex: 2, child: Text('Premium', style: _headerStyle)), + // Expanded( + // flex: 2, + // child: Text('Payment Mode', style: _headerStyle), + // ), + // Expanded( + // flex: 2, + // child: Text('Policy Number', style: _headerStyle), + // ), + // Expanded(flex: 2, child: Text('Status', style: _headerStyle)), + // Expanded(flex: 1, child: Text('Action', style: _headerStyle)), + // ], + // ), + // ), ResponsiveLayout.isMobile(context) ? _buildDataTable(context) : Expanded(child: _buildDataTable(context)), @@ -933,6 +2372,37 @@ class EnquiryHandlerState extends ConsumerState { ); } + List _buildPopupDownload(BuildContext context, selectId, editing) { + bool isMobile = ResponsiveLayout.isMobile(context); + return [ + editing + ? Container( + // color: Colors.red.shade50, + padding: EdgeInsets.only(top: 10), + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + + children: [ + buildUploadRCDocument(context), + SizedBox(width: 3), + buildUploadIDDocument(context), + SizedBox(width: 3), + buildUploadPolicyDocument(context), + ], + ), + ) + : Row( + children: [ + buildDocRC(context, selectId), + SizedBox(width: 3), + buildDocIdProof(context, selectId), + SizedBox(width: 3), + buildDocPrevPolicy(context, selectId), + ], + ), + ]; + } + Widget _buildDataTable(BuildContext context) { if (filteredData.isEmpty) { return const SizedBox( @@ -942,407 +2412,355 @@ class EnquiryHandlerState extends ConsumerState { } final sortedData = [..._paginatedData]; + final startIndex = (currentPage - 1) * itemsPerPage; + final isDesktop = !ResponsiveLayout.isMobile(context); - if (ResponsiveLayout.isMobile(context)) { - // Use Column instead of ListView - return Column( - children: List.generate(sortedData.length, (index) { - final startIndex = (currentPage - 1) * itemsPerPage; - final item = sortedData[index]; - final sno = startIndex + index; - return _buildDataCard(item, sno); - }), - ); - } + // Build both-direction scrollable DataTable + return LayoutBuilder( + builder: (context, constraints) { + double minWidth = isDesktop ? constraints.maxWidth : 1300; - // Desktop: keep ListView.builder - return ListView.builder( - itemCount: sortedData.length + 1, - itemBuilder: (context, index) { - if (index == 0) return _buildHeader(); - final startIndex = (currentPage - 1) * itemsPerPage; - final item = sortedData[index - 1]; - final sno = startIndex + index; - return _buildDataRow(item, sno); + return SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: SingleChildScrollView( + scrollDirection: Axis.vertical, + child: ConstrainedBox( + constraints: BoxConstraints(minWidth: minWidth), + child: DataTable( + headingRowColor: WidgetStatePropertyAll(Color(0xFFEDF6F5)), + dividerThickness: 0.5, + headingRowHeight: 30, + columnSpacing: isDesktop ? 20.0 : 16.0, + border: TableBorder( + horizontalInside: BorderSide( + width: 0.5, + color: Colors.grey.shade200, + ), + ), + columns: const [ + // DataColumn(label: Text('S.No', style: _headerStyle)), + DataColumn(label: Text('Received Date', style: _headerStyle)), + DataColumn(label: Text('Partner *', style: _headerStyle)), + DataColumn(label: Text('Assigned To *', style: _headerStyle)), + DataColumn(label: Text('Broker *', style: _headerStyle)), + DataColumn(label: Text('Insurer *', style: _headerStyle)), + DataColumn( + label: Text('Insured Name *', style: _headerStyle), + ), + DataColumn(label: Text('Vehicle No *', style: _headerStyle)), + DataColumn( + label: Text('Vehicle Type *', style: _headerStyle), + ), + DataColumn(label: Text('Email', style: _headerStyle)), + DataColumn(label: Text('Mobile', style: _headerStyle)), + DataColumn(label: Text('Documents', style: _headerStyle)), + DataColumn(label: Text('Remarks', style: _headerStyle)), + DataColumn(label: Text('Action', style: _headerStyle)), + DataColumn(label: Text('Assigned Date', style: _headerStyle)), + DataColumn(label: Text('Premium', style: _headerStyle)), + DataColumn(label: Text('Payment Mode', style: _headerStyle)), + DataColumn(label: Text('Policy Number', style: _headerStyle)), + DataColumn(label: Text('Status', style: _headerStyle)), + ], + rows: sortedData.asMap().entries.map((entry) { + final index = entry.key; + final item = entry.value; + final sno = startIndex + index + 1; + return _buildDataRow(item, sno); + }).toList(), + ), + ), + ), + ); }, ); } - // Widget _buildDataTable(BuildContext context) { - // if (filteredData.isEmpty) { - // return const SizedBox( - // height: 50, - // child: Center(child: Text('No available data')), - // ); - // } - // - // final sortedData = [..._paginatedData]; - // return ListView.builder( - // itemCount: ResponsiveLayout.isMobile(context) - // ? sortedData - // .length // only cards for mobile - // : sortedData.length + 1, // +1 for header in desktop - // itemBuilder: (context, index) { - // if (!ResponsiveLayout.isMobile(context) && index == 0) { - // return _buildHeader(); - // } - // - // final startIndex = (currentPage - 1) * itemsPerPage; - // final item = - // sortedData[index - (ResponsiveLayout.isMobile(context) ? 0 : 1)]; - // final sno = startIndex + index; - // - // return !ResponsiveLayout.isMobile(context) - // ? _buildDataRow(item, sno) - // : _buildDataCard(item, sno); - // }, - // ); - // } + DataRow _buildDataRow(Map item, int sno) { + final id = int.tryParse(item['id'].toString()) ?? 0; + final selectId = item['id'].toString() ?? '0'; - Widget _buildHeader() { - return SizedBox.shrink(); - } + print('idPrimary - $idPrimary'); + final editing = isEditingRow[id] ?? false; + final controllers = rowControllers[id]; - Widget _buildDataRow(Map item, sno) { - return Container( - padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 16), - // margin: const EdgeInsets.only(top: 10), - decoration: BoxDecoration( - color: Colors.white, - // color: Color(0xFFE0F7F9), - border: const Border( - bottom: BorderSide(color: Color(0xFFEAEAEA), width: 1), + return DataRow( + cells: [ + // S.No. + // DataCell(Text('$sno', style: _dataBold)), + + // Created On + DataCell( + Row( + children: [ + // Text(item['created_on'], style: _dataBold), + Text( + formatDateTimeForTable(item['created_on'], newLine: true), + style: _dataBold, + ), + ], + ), ), - borderRadius: BorderRadius.circular(8), - ), - child: Row( - children: [ - Expanded( - flex: 2, - child: Text( - _formatDate(item['created_on']) ?? '-', - style: _dataBold, - softWrap: true, - maxLines: 3, - ), - ), - Expanded( - flex: 2, - child: Text(item['agent_name'] ?? '-', style: _dataBold), - ), - Expanded( - flex: 2, - child: Text(item['assigned_to_name'] ?? '-', style: _dataBold), - ), - Expanded( - flex: 3, - child: Text( - item['insurer_name'] ?? '-', - style: _dataBold, - softWrap: true, - maxLines: 3, - ), - ), - Expanded( - flex: 2, - child: Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text(item['reg_no'] ?? '-', style: _dataBold), - Text(item['insured_name'] ?? '-', style: _dataBoldsmall), - ], - ), - ), + // Agent Name + DataCell( + editing + ? buildAgentName(context) + : Text(item['agent_name'] ?? '-', style: _dataBold), + ), - // Expanded( - // flex: 2, - // - // child: Text( - // item['insured_name'] ?? '-', - // style: _dataBold, - // softWrap: true, - // maxLines: 3, - // ), - // ), - Expanded( - flex: 2, - child: Text( - _formatDate(item['updated_on']) ?? '-', - style: _dataBold, - softWrap: true, - maxLines: 3, - ), - ), - Expanded( - flex: 2, - child: Text( - item['premium_amount'] ?? '-', - style: _dataBold, - softWrap: true, - maxLines: 3, - ), - ), - Expanded( - flex: 2, - child: Text( - item['payment_mode'] ?? '-', - style: _dataBold, - softWrap: true, - maxLines: 3, - ), - ), - Expanded( - flex: 2, - child: Text( - item['policy_number'] ?? '-', - style: _dataBold, - softWrap: true, - maxLines: 3, - ), - ), + // Assigned To + DataCell( + editing + ? buildSelectStaffMem(context) + : Text(item['assigned_to_name'] ?? '-', style: _dataBold), + ), - Expanded( - flex: 2, - child: Text(item['status'] ?? '-', style: _dataBold), - ), + DataCell( + editing + ? buildBroker(context) + : Text(item['broker_name'] ?? '-', style: _dataBold), + ), - Expanded( - flex: 1, - child: Row( - children: [ - if (item['status'] == 'Awaiting Proposal') - Tooltip( - message: "Assign Staff", - child: IconButton( - icon: Icon(Icons.assignment_ind_outlined, size: 15), - onPressed: () { - handleStaff(context, item, item['id'], item['reg_no']); - }, - splashRadius: 5, - hoverColor: Colors.black12, - padding: const EdgeInsets.all(4), - constraints: const BoxConstraints(), + // Insurer Name + DataCell( + editing + ? buildInsurer(context) + : Text(item['insurer_short_name'] ?? '-', style: _dataBold), + ), + + DataCell( + editing + ? buildInsuredName(context) + : Text(item['insured_name'] ?? '-', style: _dataBold), + ), + // Vehicle No + DataCell( + editing + ? buildVehicleNumber(context) + : Text(item['reg_no'] ?? '-', style: _dataBold), + ), + DataCell( + editing + ? buildVehicleType(context) + : Text( + _wrapText(item['vehicle_type'] ?? '-', 13), + style: _dataBold, + softWrap: true, + ), + ), + + DataCell( + editing + ? buildEmail(context) + : Text(item['email'] ?? '-', style: _dataBold), + ), + DataCell( + editing + ? buildPhNumber(context) + : Text(item['mobile'] ?? '-', style: _dataBold), + ), + + DataCell( + Builder( + builder: (buttonContext) => Material( + color: Colors.white, + child: InkWell( + hoverColor: const Color(0xFFEAF6F4), + onTap: () async { + // ✅ Get overlay and button position RELATIVE to InkWell + final RenderBox button = + buttonContext.findRenderObject() as RenderBox; + final RenderBox overlay = + Overlay.of(buttonContext).context.findRenderObject() + as RenderBox; + + final Offset position = button.localToGlobal( + Offset.zero, + ancestor: overlay, + ); + + await showMenu( + context: buttonContext, + position: RelativeRect.fromLTRB( + position.dx, + position.dy + button.size.height, + overlay.size.width, + 0, ), - ), - - Tooltip( - message: 'Edit', - child: IconButton( - icon: Image.asset( - "assets/miscellaneous/Edit.png", - height: 12, - width: 12, - ), - onPressed: () { - handleEdit(item); - }, - splashRadius: 5, - hoverColor: Colors.black12, - padding: const EdgeInsets.all(4), - constraints: const BoxConstraints(), + items: [ + PopupMenuItem( + child: Column( + mainAxisSize: MainAxisSize.min, + children: _buildPopupDownload( + buttonContext, + selectId, + editing, + ), + ), + ), + ], + color: Colors.white, + ); + }, + child: Tooltip( + message: editing + ? 'Click To Upload Documents' + : 'Click To View Documents', + waitDuration: const Duration(milliseconds: 500), + showDuration: const Duration(seconds: 2), + child: Icon( + editing ? Icons.upload_file : Icons.download_for_offline, + color: editing ? Colors.blue : Colors.green, + size: 22, ), ), - ], + ), ), ), - ], - ), - ); - } + ), - Widget _buildDataCard(Map item, int sno) { - return Container( - margin: const EdgeInsets.symmetric(vertical: 6, horizontal: 8), - padding: const EdgeInsets.all(12), - decoration: BoxDecoration( - color: const Color(0xFFF6FEFD), - borderRadius: BorderRadius.circular(8.0), - border: Border.all(color: const Color(0xffD9EBE8)), - ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - /// Top row: Reg.No + Action menu - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text(item['reg_no'] ?? '-', style: _headerStyle), - PopupMenuButton( - color: Colors.white, - padding: EdgeInsets.zero, - offset: Offset(0, 30), - icon: Icon(Icons.more_vert, color: Color(0xFF475569), size: 14), - itemBuilder: (context) => [ - // CustomPopupMenuEntry( - // child: Container( - // padding: EdgeInsets.symmetric(horizontal: 8, vertical: 8), - // child: Column( - // mainAxisSize: MainAxisSize.min, - // children: _buildPopupMenuActions( - // context, - // item, - // item['id'], - // item['reg_no'], - // ), - // ), - // ), - // ), - ], - ), - ], - ), - const Divider(color: Color(0xffD9EBE8), thickness: 0.8), + DataCell( + editing + ? buildRemarks(context) + : Tooltip( + message: item['remarks'] ?? '-', // full content on hover + waitDuration: const Duration(milliseconds: 400), + showDuration: const Duration(seconds: 2), + child: Text( + _truncateText(item['remarks'] ?? '-', 10), + style: _dataBold, + overflow: TextOverflow.ellipsis, + ), + ), + ), - /// Company + Status - Row( - children: [ - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, + // Actions + DataCell( + editing + ? Row( children: [ - const Text("Company", style: _cardheaderStyle), - Text(item['insurer_name'] ?? '-', style: _cardBodyStyle), - ], - ), - ), - const SizedBox(width: 5), - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const Text("Status", style: _cardheaderStyle), - Text(item['status'] ?? '-', style: _cardBodyStyle), - ], - ), - ), - ], - ), - const SizedBox(height: 15), - - /// Agents + Assigned To - Row( - children: [ - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const Text("Partner", style: _cardheaderStyle), - Text(item['agent_name'] ?? '-', style: _cardBodyStyle), - ], - ), - ), - const SizedBox(width: 5), - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const Text("Assigned To", style: _cardheaderStyle), - Text( - item['assigned_to_name'] ?? '-', - style: _cardBodyStyle, + IconButton( + icon: const Icon(Icons.check, color: Colors.green), + tooltip: "Save", + onPressed: () => handleSave(id), + ), + IconButton( + icon: const Icon(Icons.close, color: Colors.red), + tooltip: "Cancel", + onPressed: () => handleCancel(id), ), ], - ), - ), - ], - ), - const SizedBox(height: 15), - - /// Insured Name + Premium - Row( - children: [ - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, + ) + : Row( children: [ - const Text("Insured Name", style: _cardheaderStyle), - Text(item['insured_name'] ?? '-', style: _cardBodyStyle), - ], - ), - ), - const SizedBox(width: 5), - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const Text("Premium", style: _cardheaderStyle), - Text(item['premium_amount'] ?? '-', style: _cardBodyStyle), - ], - ), - ), - ], - ), - const SizedBox(height: 15), - - /// Payment Mode + Policy Number - Row( - children: [ - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const Text("Payment Mode", style: _cardheaderStyle), - Text(item['payment_mode'] ?? '-', style: _cardBodyStyle), - ], - ), - ), - const SizedBox(width: 5), - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const Text("Policy Number", style: _cardheaderStyle), - Text(item['policy_number'] ?? '-', style: _cardBodyStyle), - ], - ), - ), - ], - ), - const SizedBox(height: 15), - - /// Date + Remarks - Row( - children: [ - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const Text("Date", style: _cardheaderStyle), - Text( - _formatDate(item['updated_on']) ?? '-', - style: _cardBodyStyle, + // if (roleId == 'manager') ...[ + // if (item['status'] == 'Awaiting Proposal') + // Tooltip( + // message: "Assign Staff", + // child: IconButton( + // icon: Icon(Icons.assignment_ind_outlined, size: 15), + // onPressed: () { + // handleStaff( + // context, + // item, + // item['id'], + // item['reg_no'], + // ); + // }, + // splashRadius: 5, + // hoverColor: Colors.black12, + // padding: const EdgeInsets.all(4), + // constraints: const BoxConstraints(), + // ), + // ), + // ], + IconButton( + icon: const Icon(Icons.edit, color: Colors.blue), + tooltip: "Edit", + onPressed: () => handleEdit(item), ), + // IconButton( + // icon: const Icon(Icons.delete, color: Colors.red), + // tooltip: "Delete", + // onPressed: () => setState(() { + // getStaffData.removeWhere((e) => e['id'] == item['id']); + // }), + // ), ], ), - ), - const SizedBox(width: 5), - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const Text("Remarks", style: _cardheaderStyle), - Text( - item['remarks'] ?? '-', - style: _cardBodyStyle, - maxLines: 3, - ), - ], - ), - ), - ], + ), + + // Assigned Date (formatted) + DataCell( + Text( + // item['updated_on'] ?? '-', + formatDateTimeForTable(item['updated_on'], newLine: true), + style: _dataBold, + softWrap: true, + maxLines: 3, ), - ], - ), + ), + + // Premium + DataCell( + // ((item['premium_amount'] != null) && + // (item['premium_amount'] != '') && + // ((item['status'] != 'Awaiting Proposal') || + // (item['status'] == 'Proposal Created') || + // (item['status'] == 'Proposal Rejected'))) + (item['status'] == 'Proposal Created' || + item['status'] == 'Proposal Rejected') + ? InkWell( + onTap: () { + // handle approval logic + + handleProposalAccept(context, item['id']); + }, + child: Text( + 'Click To\nApprove', + style: GoogleFonts.inter( + color: Colors.green, + fontSize: 11, + fontWeight: FontWeight.w700, + ), + ), + ) + : Text( + item['premium_amount']?.toString() ?? '-', + style: _dataBold, + ), + ), + + // Payment Mode + DataCell(Text(item['payment_mode'] ?? '-', style: _dataBold)), + + // Policy Number + DataCell( + Text( + item['policy_number'] ?? '-', + style: _dataBold, + softWrap: true, + maxLines: 3, + ), + ), + + // Status + DataCell(Text(item['status'] ?? '-', style: _dataBold)), + ], ); } static final _dataBold = TextStyle( - fontSize: 14, + fontSize: 12, + fontWeight: FontWeight.w500, + color: Colors.black, + // color: Color(0xFF000000), + ); + final _tableDataTimeStyle = GoogleFonts.inter( + fontSize: 10, fontWeight: FontWeight.w400, - color: Color(0xFF000000), ); static final _dataBoldsmall = TextStyle( fontSize: 12, @@ -1359,7 +2777,7 @@ class EnquiryHandlerState extends ConsumerState { static const _headerStyle = TextStyle( color: Colors.black, - fontWeight: FontWeight.bold, + fontWeight: FontWeight.w500, ); static const _cardheaderStyle = TextStyle( color: Colors.black, @@ -1367,9 +2785,68 @@ class EnquiryHandlerState extends ConsumerState { fontSize: 12, ); + static final TextStyle _textStyle = TextStyle( + fontSize: 12, + fontWeight: FontWeight.w600, + ); + static const _cardBodyStyle = TextStyle( color: Color(0xFF545454), fontWeight: FontWeight.w400, fontSize: 12, ); + + String _truncateText(String text, int limit) { + if (text.length <= limit) return text; + return '${text.substring(0, limit)}...'; + } + + String _wrapText(String text, int chunkSize) { + if (text.isEmpty) return '-'; + final buffer = StringBuffer(); + for (int i = 0; i < text.length; i += chunkSize) { + int end = (i + chunkSize < text.length) ? i + chunkSize : text.length; + buffer.writeln(text.substring(i, end)); // adds '\n' after each chunk + } + return buffer.toString().trim(); + } + + String formatDateTimeForTable(String? input, {bool newLine = false}) { + if (input == null || input.trim().isEmpty) return '-'; + + final s = input.trim(); + DateTime dt; + + try { + // Case 1: ISO-like (yyyy-MM-dd ...), DateTime.parse will handle + if (RegExp(r'^\d{4}-\d{2}-\d{2}').hasMatch(s)) { + dt = DateTime.parse(s); + } + // Case 2: day-month-year at start (e.g. 13-11-2025 07:28 AM or 13-11-2025 07:28) + else if (RegExp(r'^\d{2}-\d{2}-\d{4}').hasMatch(s)) { + // Try with am/pm (hh:mm a) + if (RegExp(r'\b(am|pm)\b', caseSensitive: false).hasMatch(s)) { + dt = DateFormat('dd-MM-yyyy hh:mm a').parse(s); + } + // Try 24-hour time "dd-MM-yyyy HH:mm" or just date "dd-MM-yyyy" + else if (RegExp(r'^\d{2}-\d{2}-\d{4}\s+\d{1,2}:\d{2}').hasMatch(s)) { + dt = DateFormat('dd-MM-yyyy HH:mm').parse(s); + } else { + dt = DateFormat('dd-MM-yyyy').parse(s); + } + } + // Case 3: fallback — try parse with DateTime.parse (may throw) + else { + dt = DateTime.parse(s); + } + + final dateStr = DateFormat('dd-MM-yyyy').format(dt); + final timeStr = DateFormat('hh:mm a').format(dt); // e.g. 07:28 AM + + return newLine ? '$dateStr\n$timeStr' : '$dateStr | $timeStr'; + } catch (e) { + // If parsing fails, return original string as fallback (or '-' if empty) + return s.isNotEmpty ? s : '-'; + } + } } diff --git a/lib/presentation/screens/handler/enquiryListOld.dart b/lib/presentation/screens/handler/enquiryListOld.dart new file mode 100644 index 0000000..fe32a1e --- /dev/null +++ b/lib/presentation/screens/handler/enquiryListOld.dart @@ -0,0 +1,1375 @@ +// import 'dart:io' as html; +import 'package:http/http.dart' as http; +import 'package:dropdown_search/dropdown_search.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:go_router/go_router.dart'; +import 'package:google_fonts/google_fonts.dart'; +import 'package:intl/intl.dart'; +import 'package:shared_preferences/shared_preferences.dart'; + +import '../../../../core/routing/routes.dart'; + +import '../../../../core/services/api_service.dart'; +import 'package:universal_html/html.dart' as html; + +import '../../../../data/utils/Pagination.dart'; + +import '../../layouts/main_layout.dart'; +import '../../layouts/responsive_layout.dart'; +import '../../providers/manager_provider.dart'; +import '../../providers/quotation_staff_proivder.dart'; +import '../../providers/userRoleProvider.dart'; +import '../../themes/indicators/export_btn.dart'; +import '../../themes/indicators/search_field_theme.dart'; +import '../../widgets/custom_Stdate_EnDate_Filter.dart'; +import '../../widgets/custom_action_popup.dart'; +import '../staff/assignStaff.dart'; + +class EnquiryListInline extends ConsumerStatefulWidget { + const EnquiryListInline({super.key}); + @override + ConsumerState createState() => EnquiryHandlerState(); +} + +class EnquiryHandlerState extends ConsumerState { + int currentPage = 1; + int itemsPerPage = 10; + late ApiService apiService; + dynamic userId; + dynamic managerId; + dynamic dashboardKey; + dynamic SelectedStatus; + dynamic SelectedStaffId; + dynamic handlerId; + final _formKey = GlobalKey(); + + // List> dataVal = []; + List> getStaffData = []; + List> originalData = []; + List> filteredData = []; + bool isLoading = false; + dynamic roleId; + + Map controllers = {}; + List tabHeader = ['startDate', 'endDate']; + + @override + void initState() { + super.initState(); + + apiService = ApiService(); + + for (String field in tabHeader) { + controllers[field] = TextEditingController(); + } + + Future.microtask(() async { + final id = ref.read(managerIdProvider); + roleId = ref.read(userRoleProvider); + userId = ref.read(userIdProvider); + managerId = ref.read(managerIdProvider); + + // dashboardKey = ref.read(dashboardKeyProvider); + // final dashboardStatus = ref.read(dashboardStatusProvider); + // final dashboardStaffId = ref.read(dashboardStaffIdProvider); + final prefs = await SharedPreferences.getInstance(); + dashboardKey = prefs.getString('dashboardKeyProvider'); + final dashboardStatus = prefs.getString('dashboardStatusProvider'); + final dashboardStaffId = prefs.getString('dashboardStaffIdProvider'); + // handlerId = ref.read(handlerIdProvider); + + print('handlerIdENQ - $handlerId'); + print("C72 => r : $roleId | mId: $id | uId: $userId !mID : $managerId "); + + print('dashboardKey - $dashboardKey'); + print('dashboardStatus - $dashboardStatus'); + print('dashboardStaffId - $dashboardStaffId'); + + if (managerId != null && + dashboardKey != null && + dashboardStatus != null && + dashboardStaffId != null) { + print("DASHBOARD STATus-$dashboardStatus -- $dashboardStaffId -"); + SelectedStatus = dashboardStatus; + SelectedStaffId = dashboardStaffId; + getStaffList( + managerId, + roleId, + SelectedStatus: dashboardStatus, + SelectedStaffId: dashboardStaffId, + ); + } else { + print('ELSE'); + getStaffList(managerId, roleId); + } + }); + } + + void refresh() { + final id = ref.read(managerIdProvider); + final roleId = ref.read(userRoleProvider); + final userId = ref.read(userIdProvider); + + print("C82 => r : $roleId | mId: $id | uId: $userId "); + if (managerId != null) { + getStaffList(managerId, roleId); + } + } + + void filterDateRange() { + // print('SelectedStatus - $SelectedStatus'); + // // ✅ Validate the form first + // if (!_formKey.currentState!.validate()) { + // // stop execution if validation fails + // return; + // } + // + // final fromDateText = controllers['startDate']?.text ?? ''; + // final toDateText = controllers['endDate']?.text ?? ''; + // + // // Optional: double-check End >= Start + // final fromDate = DateFormat('dd-MM-yyyy').parse(fromDateText); + // final toDate = DateFormat('dd-MM-yyyy').parse(toDateText); + // + // if (toDate.isBefore(fromDate)) { + // // This is already caught by the validator, but extra safety + // ScaffoldMessenger.of(context).showSnackBar( + // SnackBar(content: Text("End Date cannot be earlier than Start Date")), + // ); + // return; + // } + + // ✅ Call your API + getStaffList( + managerId, + roleId, + SelectedStatus: SelectedStatus ?? '', + SelectedStaffId: SelectedStaffId ?? '', + ); + } + + Future refrshfilterDateRange() async { + // setState(() async { + final prefs = await SharedPreferences.getInstance(); + + // ✅ Clear any old data + await prefs.remove('dashboardKeyProvider'); + await prefs.remove('dashboardStatusProvider'); + await prefs.remove('dashboardStaffIdProvider'); + SelectedStatus = ''; + SelectedStaffId = null; + controllers['startDate']!.clear(); + controllers['endDate']!.clear(); + + controllers['startDate']?.text = ''; + controllers['endDate']?.text = ''; + // Reset the FormField validation + _formKey.currentState?.reset(); + // }); + getStaffList( + managerId, + roleId, + SelectedStatus: SelectedStatus ?? '', + SelectedStaffId: SelectedStaffId ?? '', + ); + } + + Future getStaffList( + int managerId, + role, { + String fromDate = '', + String toDate = '', + String SelectedStatus = '', + String SelectedStaffId = '', + }) async { + print('A72 => Fns called => $managerId | $role'); + setState(() { + isLoading = true; + }); + + dynamic fromDt; + dynamic toDt; + + final fromDateVal = controllers['startDate']?.text ?? ''; + final toDateVal = controllers['endDate']?.text ?? ''; + final dateFormat = DateFormat('dd-MM-yyyy'); + + if (dashboardKey != 'fromDashboard' && + fromDateVal == '' && + toDateVal == '') { + print("ENQ LIST PAGE"); + + final today = DateTime.now(); + fromDt = dateFormat.format(today.subtract(Duration(days: 15))); + toDt = dateFormat.format(today); + + print('Enq FROM TO - $fromDt - $toDt'); + } else { + fromDt = fromDateVal; + toDt = toDateVal; + } + + try { + final response = await apiService.fetchEnquiryList( + managerId, + role, + fromDate: fromDt, + toDate: toDt, + selectedStatus: SelectedStatus != null ? SelectedStatus : '', + selectedStaffId: SelectedStaffId ?? '', + ); + + if (response['status'] == 'success') { + final data = response['data']; + final fromDate = response['from_date'] ?? ''; + final toDate = response['to_date'] ?? ''; + + print('FromDate : $fromDate'); + print('ToDate : $toDate'); + + setState(() { + controllers['startDate']?.text = fromDate; + controllers['endDate']?.text = toDate; + if (data is List) { + getStaffData = List>.from(data); + } else if (data is Map) { + getStaffData = [Map.from(data)]; + } else { + getStaffData = []; + } + originalData = getStaffData; + filteredData = List.from(originalData); + }); + } else { + getStaffData = []; + originalData = []; + } + } catch (e) { + print('Exception occurred: $e'); + } finally { + setState(() { + isLoading = false; + }); + } + } + + // Future getStaffList1( + // int managerId, + // role, { + // String fromDate = '', + // String toDate = '', + // }) async { + // print('C89 => Fns called => $managerId | $role'); + // setState(() { + // isLoading = true; + // }); + // + // try { + // final response = await apiService.fetchPolicyList( + // managerId, + // role, + // fromDate: controllers['startDate']?.text ?? '', + // toDate: controllers['endDate']?.text ?? '', + // ); + // + // print('FromDate : $fromDate'); + // print('ToDate : $toDate'); + // + // if (response['status'] == 'success') { + // final data = response['data']; + // print('C99 => getStaffListData => ${response['data']}'); + // final fromDate = response['from_date'] ?? ''; + // final toDate = response['to_date'] ?? ''; + // + // print('FromDate : $fromDate'); + // print('ToDate : $toDate'); + // setState(() { + // controllers['startDate']?.text = fromDate; + // controllers['endDate']?.text = toDate; + // + // if (data is List) { + // // Already a list of maps + // getStaffData = List>.from(data); + // } else if (data is Map) { + // // Single object, wrap in a list + // getStaffData = [Map.from(data)]; + // } else { + // getStaffData = []; + // } + // // getStaffData = List>.from(response['data']); + // originalData = getStaffData; + // filteredData = List.from(originalData); + // // print('originalData - $getClaimPolicies'); + // }); + // } else { + // getStaffData = []; + // originalData = []; + // } + // } catch (e) { + // print('Exception occurred: $e'); + // } finally { + // setState(() { + // isLoading = false; + // }); + // } + // } + + List get _paginatedData { + // Sort descending by id first + final sortedData = [...filteredData] + ..sort((a, b) => int.parse(b['id']) - int.parse(a['id'])); + + if (sortedData.isEmpty) return []; + + // Ensure currentPage is valid + final maxPage = (sortedData.length / itemsPerPage).ceil(); + final safePage = currentPage.clamp(1, maxPage); + + final startIndex = (safePage - 1) * itemsPerPage; + final endIndex = (startIndex + itemsPerPage).clamp(0, sortedData.length); + + return sortedData.sublist(startIndex, endIndex); + } + + void filterData(String query) { + print("FilterDAta - $query"); + setState(() { + filteredData = getStaffData.where((item) { + // final isActiveStatus = item['is_active'] == "1" ? "active" : "inactive"; + + return (item['agent_name'] ?? '-').toLowerCase().contains( + query.toLowerCase(), + ) || + (item['reg_no'] ?? '-').toLowerCase().contains( + query.toLowerCase(), + ) || + (item['insurer_name'] ?? '-').toLowerCase().contains( + query.toLowerCase(), + ) || + (item['assigned_to_name'] ?? '-').toLowerCase().contains( + query.toLowerCase(), + ) || + (item['insured_name'] ?? '-').toLowerCase().contains( + query.toLowerCase(), + ) || + (item['premium_amount'] ?? '-').toLowerCase().contains( + query.toLowerCase(), + ) || + (item['payment_mode'] ?? '-').toLowerCase().contains( + query.toLowerCase(), + ) || + (_formatDate(item['created_on']) ?? '-').toLowerCase().contains( + query.toLowerCase(), + ) || + (_formatDate(item['updated_on']) ?? '-').toLowerCase().contains( + query.toLowerCase(), + ) || + (item['policy_number'] ?? '-').toLowerCase().contains( + query.toLowerCase(), + ) || + (item['status'] ?? '-').toLowerCase().contains(query.toLowerCase()); + }).toList(); + }); + } + + final TextEditingController _searchStaffController = TextEditingController(); + + String _formatDate(String rawDate) { + try { + final dateTime = DateTime.parse(rawDate); + return DateFormat( + 'dd-MM-yyyy HH:mm', + ).format(dateTime); // 24-hour format + } catch (e) { + return rawDate; // fallback if parsing fails + } + } + + Future handleStaff( + BuildContext context, + dynamic data, + id, + regNum, + ) async { + showDialog( + context: context, + builder: (ctx) => AssignStaffDialog( + enquiryPrimaryId: id, + regNum: regNum, + userId: userId, + onSubmit: (value) { + debugPrint("New assignY: $value"); + refresh(); + }, + ), + ); + } + + Future handleEdit(item) async { + // Navigator.pop(context); + print('EDITStaff - ${item['id']}'); + + final prefs = await SharedPreferences.getInstance(); + + await prefs.remove('enqAgentDataId'); + + final id = item['id'].toString(); + // ✅ Save the new id + await prefs.setString('enqAgentDataId', id.toString()); + ref.read(enquiryIdProvider.notifier).state = id; + + context.go(AppRoutes.tabEnquiry); + } + + // List _buildPopupMenuActions( + // BuildContext context, + // dynamic data, + // id, + // regNum, + // ) { + // return [ + // if (roleId == 'manager' && data['status'] == 'Awaiting Quotation') ...[ + // Material( + // color: Colors.transparent, + // child: InkWell( + // onTap: () { + // Navigator.pop(context); + // showDialog( + // context: context, + // builder: (ctx) => AssignStaffDialog( + // enquiryPrimaryId: id, + // regNum: regNum, + // userId: 1, + // onSubmit: (value) { + // debugPrint("New assignY: $value"); + // refresh(); + // }, + // ), + // ); + // }, + // hoverColor: Color(0xFFE3F1F0), + // splashColor: Color(0xFFE3F1F0), + // borderRadius: BorderRadius.circular(6), + // child: Padding( + // padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4), + // child: Row( + // // mainAxisSize: MainAxisSize.min, + // children: [ + // Image.asset( + // "assets/miscellaneous/image_3.png", + // // height: 45, + // // width: 15, + // ), + // + // SizedBox(width: 10), + // Text('Assign Staff'), + // ], + // ), + // ), + // ), + // ), + // ], + // + // Material( + // color: Colors.transparent, + // child: InkWell( + // onTap: () async { + // Navigator.pop(context); + // final prefs = await SharedPreferences.getInstance(); + // + // // ✅ Remove old value (if any) + // await prefs.remove('enqStaffDataId'); + // + // // ✅ Save the new id + // await prefs.setString('enqStaffDataId', id.toString()); + // + // // Read it back if needed + // // final dynamic? enqStaffDataId = prefs.getString('enqStaffDataId'); + // + // // ✅ Update provider too + // // ref.read(quotationStaffIdProvider.notifier).state = enqStaffDataId; + // ref.read(quotationStaffIdProvider.notifier).state = id; + // + // context.go(AppRoutes.quotation); + // }, + // hoverColor: Color(0xFFE3F1F0), + // splashColor: Color(0xFFE3F1F0), + // borderRadius: BorderRadius.circular(6), + // child: Padding( + // padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4), + // child: Row( + // // mainAxisSize: MainAxisSize.min, + // children: [ + // Image.asset( + // "assets/miscellaneous/image_1.png", + // height: 15, + // width: 15, + // ), + // + // const SizedBox(width: 10), + // ((data['status'] == 'Policy Created') || + // (data['status'] == 'Quotation Accepted')) + // ? const Text('View Quotation') + // : const Text('Create Quotation'), + // ], + // ), + // ), + // ), + // ), + // + // if (data['status'] == 'Quotation Accepted' || + // data['status'] == 'Policy Created') + // Material( + // color: Colors.transparent, + // child: InkWell( + // onTap: () async { + // Navigator.pop(context); + // + // final prefs = await SharedPreferences.getInstance(); + // + // // ✅ Remove old value (if any) + // await prefs.remove('enqStaffDataId'); + // + // // ✅ Save the new id + // await prefs.setString('enqStaffDataId', id.toString()); + // + // // ✅ Update provider too + // + // ref.read(quotationStaffIdProvider.notifier).state = id; + // context.go(AppRoutes.policy); + // }, + // hoverColor: Color(0xFFE3F1F0), + // splashColor: Color(0xFFE3F1F0), + // borderRadius: BorderRadius.circular(6), + // child: Padding( + // padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4), + // child: Row( + // // mainAxisSize: MainAxisSize.min, + // children: [ + // Image.asset( + // "assets/miscellaneous/image_2.png", + // height: 15, + // width: 15, + // ), + // + // const SizedBox(width: 10), + // data['status'] == 'Policy Created' + // ? Text('View Policy') + // : Text('Create Policy'), + // ], + // ), + // ), + // ), + // ), + // ]; + // } + + @override + Widget build(BuildContext context) { + return MainLayout( + title: "Enquiries", + body: Container( + // color: Colors.yellow.shade50, + width: MediaQuery.of(context).size.width, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Container( + height: 30, + width: MediaQuery.of(context).size.width, + child: GestureDetector( + onTap: () { + context.go(AppRoutes.dashboard); + }, + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Tooltip( + message: 'Back', + child: IconButton( + icon: const Icon(Icons.arrow_left_sharp, size: 25), + onPressed: () async { + context.go(AppRoutes.dashboard); + final prefs = await SharedPreferences.getInstance(); + await prefs.remove('dashboardKeyProvider'); + await prefs.remove('dashboardStatusProvider'); + await prefs.remove('dashboardStaffIdProvider'); + }, + splashRadius: 18, + padding: const EdgeInsets.all(4), + hoverColor: Colors.black12, + constraints: const BoxConstraints(), + ), + ), + const SizedBox(width: 5), // spacing between icon and text + Text( + "Enquiries", + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.w600, + ), + ), + ], + ), + ), + ), + SizedBox(height: 5), + ResponsiveLayout.isMobile(context) + ? Container( + height: MediaQuery.of(context).size.height * 0.69, + child: SingleChildScrollView( + child: Padding( + padding: EdgeInsets.all(8), + child: _buildContent(context), + ), + ), + ) + : Expanded( + child: Container( + width: MediaQuery.of(context).size.width, + padding: EdgeInsets.all(8.0), + child: _buildContent(context), + ), + ), + + // Expanded( + // child: Container( + // // color: Colors.green, + // // color: Colors.green.shade50, + // width: MediaQuery.of(context).size.width, + // + // padding: EdgeInsets.all(8.0), + // child: + // ), + // ), + Container( + // height: 20, + width: MediaQuery.of(context).size.width, + // color: Colors.green.shade50, + child: PaginationControls( + currentPage: currentPage, + itemsPerPage: itemsPerPage, + // totalItems: dataVal.length, + totalItems: filteredData.length, + // activeColor: layoutColor, // your theme color + onPageChanged: (page) { + setState(() { + currentPage = page; + }); + }, + onItemsPerPageChanged: (items) { + setState(() { + itemsPerPage = items; + currentPage = 1; + }); + }, + ), + ), + ], + ), + ), + ); + } + + Widget _buildContent(BuildContext context) { + return Column( + children: [ + if (ResponsiveLayout.isMobile(context)) ...[ + Container( + padding: EdgeInsets.all(8.0), + color: Color(0xffD9EBE8), + child: DateFilterRow( + key: ValueKey(SelectedStatus ?? ''), + onFilterStaff: (val) { + print('Selected Filterd STAFF Id - $val'); + SelectedStaffId = val; + }, + selectedStaffId: SelectedStaffId, + role: roleId, + id: userId, + selectedStatusVal: SelectedStatus, + startController: controllers['startDate']!, + endController: controllers['endDate']!, + formKey: _formKey, + isMobile: ResponsiveLayout.isMobile(context), + onStatusChanged: (val) { + SelectedStatus = val; // update parent + }, + + onFilter: () { + // call your filter logic + filterDateRange(); + }, + onRefresh: () { + // call your refresh logic + refrshfilterDateRange(); + }, + ), + ), + ], + + Container( + // height: 40, + // color: Colors.pink, + child: Row( + crossAxisAlignment: CrossAxisAlignment.end, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + if (!ResponsiveLayout.isMobile(context)) ...[ + DateFilterRow( + key: ValueKey(SelectedStatus ?? ''), + role: roleId, + id: userId, + selectedStatusVal: SelectedStatus, + onFilterStaff: (val) { + print('Selected Filterd STAFF Id - $val'); + SelectedStaffId = val; + }, + selectedStaffId: SelectedStaffId, + startController: controllers['startDate']!, + endController: controllers['endDate']!, + onStatusChanged: (val) { + SelectedStatus = val; // update parent + }, + formKey: _formKey, + isMobile: ResponsiveLayout.isMobile(context), + onFilter: () { + // call your filter logic + filterDateRange(); + }, + onRefresh: () { + // call your refresh logic + refrshfilterDateRange(); + }, + ), + + Spacer(), + ], + + ThemedSearchField( + hintText: 'Search', + backgroundColor: Color(0xFFF6F8F8), + onChanged: filterData, + controller: _searchStaffController, + txtwidth: ResponsiveLayout.isMobile(context) + ? MediaQuery.of(context).size.width * 0.7 + : MediaQuery.of(context).size.width * 0.13, + ), + + ResponsiveLayout.isMobile(context) + ? Spacer() + : SizedBox(width: 10), + + ExportBtn( + sheetName: "Enquiry", + fileName: "Enquiry_list", + data: filteredData, + txt: !ResponsiveLayout.isMobile(context) ? true : false, + displayHeaders: [ + "Received Date & Time", + "Partner", + "Assigned To", + "Insurer", + "Vehicle.No.", + "Insured Name", + "Assigned Date & Time", + "Premium", + "Payment Mode", + "Policy Number", + "Status", + ], + keys: [ + "created_on", + "agent_name", + "assigned_to_name", + "insurer_name", + "reg_no", + "insured_name", + "updated_on", + "premium_amount", + "payment_mode", + "policy_number", + "status", + ], + ), + SizedBox(width: 10), + GestureDetector( + onTap: () async { + final prefs = await SharedPreferences.getInstance(); + + await prefs.remove('enqAgentDataId'); + ref.read(enquiryIdProvider.notifier).state = null; + context.go(AppRoutes.tabEnquiry); + // print('Export'); + }, + child: Container( + padding: EdgeInsets.all(8.0), + decoration: BoxDecoration( + color: Color(0xFF425B5B), + borderRadius: BorderRadius.circular(8.0), + ), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Icon(Icons.add, color: Colors.white), + + if (!ResponsiveLayout.isMobile(context)) ...[ + SizedBox(width: 10), + Text( + 'Raise Enquiry', + style: GoogleFonts.inter( + color: Colors.white, + fontWeight: FontWeight.w600, + fontSize: 14, + ), + ), + ], + ], + ), + ), + ), + // SizedBox(width: 10), + // GestureDetector( + // onTap: () { + // // print('Export'); + // }, + // child: Container( + // padding: EdgeInsets.all(8.0), + // decoration: BoxDecoration( + // color: Color(0xFF425B5B), + // borderRadius: BorderRadius.circular(8.0), + // ), + // child: Row( + // mainAxisSize: MainAxisSize.min, + // children: [ + // Icon(Icons.add, color: Colors.white), + // + // if (!ResponsiveLayout.isMobile(context)) ...[ + // SizedBox(width: 10), + // + // GestureDetector( + // onTap: () { + // // context.go(AppRoutes.agent / create); + // context.go('/staff/create'); + // }, + // child: Text( + // 'Create New Staff', + // style: TextStyle( + // color: Colors.white, + // fontWeight: FontWeight.w600, + // fontSize: 14, + // ), + // ), + // ), + // ], + // ], + // ), + // ), + // ), + ], + ), + ), + SizedBox(height: 10), + if (!ResponsiveLayout.isMobile(context)) + Container( + decoration: BoxDecoration( + color: Color(0xFFEDF6F5), + borderRadius: BorderRadius.circular(6), + ), + padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 16), + child: const Row( + children: [ + Expanded( + flex: 2, + child: Text('Received Date ', style: _headerStyle), + ), + + Expanded(flex: 2, child: Text('Partner ', style: _headerStyle)), + Expanded( + flex: 2, + child: Text('Assigned To ', style: _headerStyle), + ), + Expanded(flex: 3, child: Text('Insurer ', style: _headerStyle)), + + Expanded( + flex: 2, + child: Text('Vehicle.No ', style: _headerStyle), + ), + + // Expanded( + // flex: 2, + // // child: Padding( + // // padding: EdgeInsets.only(left: 8.0), + // child: Text('Insured Name ', style: _headerStyle), + // // ), + // ), + Expanded( + flex: 2, + child: Text('Assigned Date ', style: _headerStyle), + ), + Expanded(flex: 2, child: Text('Premium ', style: _headerStyle)), + Expanded( + flex: 2, + child: Text('Payment Mode ', style: _headerStyle), + ), + Expanded( + flex: 2, + child: Text('Policy Number ', style: _headerStyle), + ), + + Expanded(flex: 2, child: Text('Status ', style: _headerStyle)), + Expanded(flex: 1, child: Text('Action', style: _headerStyle)), + ], + ), + ), + ResponsiveLayout.isMobile(context) + ? _buildDataTable(context) + : Expanded(child: _buildDataTable(context)), + ], + ); + } + + Widget _buildDataTable(BuildContext context) { + if (filteredData.isEmpty) { + return const SizedBox( + height: 50, + child: Center(child: Text('No available data')), + ); + } + + final sortedData = [..._paginatedData]; + + if (ResponsiveLayout.isMobile(context)) { + // Use Column instead of ListView + return Column( + children: List.generate(sortedData.length, (index) { + final startIndex = (currentPage - 1) * itemsPerPage; + final item = sortedData[index]; + final sno = startIndex + index; + return _buildDataCard(item, sno); + }), + ); + } + + // Desktop: keep ListView.builder + return ListView.builder( + itemCount: sortedData.length + 1, + itemBuilder: (context, index) { + if (index == 0) return _buildHeader(); + final startIndex = (currentPage - 1) * itemsPerPage; + final item = sortedData[index - 1]; + final sno = startIndex + index; + return _buildDataRow(item, sno); + }, + ); + } + + // Widget _buildDataTable(BuildContext context) { + // if (filteredData.isEmpty) { + // return const SizedBox( + // height: 50, + // child: Center(child: Text('No available data')), + // ); + // } + // + // final sortedData = [..._paginatedData]; + // return ListView.builder( + // itemCount: ResponsiveLayout.isMobile(context) + // ? sortedData + // .length // only cards for mobile + // : sortedData.length + 1, // +1 for header in desktop + // itemBuilder: (context, index) { + // if (!ResponsiveLayout.isMobile(context) && index == 0) { + // return _buildHeader(); + // } + // + // final startIndex = (currentPage - 1) * itemsPerPage; + // final item = + // sortedData[index - (ResponsiveLayout.isMobile(context) ? 0 : 1)]; + // final sno = startIndex + index; + // + // return !ResponsiveLayout.isMobile(context) + // ? _buildDataRow(item, sno) + // : _buildDataCard(item, sno); + // }, + // ); + // } + + Widget _buildHeader() { + return SizedBox.shrink(); + } + + Widget _buildDataRow(Map item, sno) { + return Container( + padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 16), + // margin: const EdgeInsets.only(top: 10), + decoration: BoxDecoration( + color: Colors.white, + // color: Color(0xFFE0F7F9), + border: const Border( + bottom: BorderSide(color: Color(0xFFEAEAEA), width: 1), + ), + borderRadius: BorderRadius.circular(8), + ), + child: Row( + children: [ + Expanded( + flex: 2, + child: Text( + _formatDate(item['created_on']) ?? '-', + style: _dataBold, + softWrap: true, + maxLines: 3, + ), + ), + + Expanded( + flex: 2, + child: Text(item['agent_name'] ?? '-', style: _dataBold), + ), + Expanded( + flex: 2, + child: Text(item['assigned_to_name'] ?? '-', style: _dataBold), + ), + Expanded( + flex: 3, + child: Text( + item['insurer_name'] ?? '-', + style: _dataBold, + softWrap: true, + maxLines: 3, + ), + ), + Expanded( + flex: 2, + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(item['reg_no'] ?? '-', style: _dataBold), + Text(item['insured_name'] ?? '-', style: _dataBoldsmall), + ], + ), + ), + + // Expanded( + // flex: 2, + // + // child: Text( + // item['insured_name'] ?? '-', + // style: _dataBold, + // softWrap: true, + // maxLines: 3, + // ), + // ), + Expanded( + flex: 2, + child: Text( + _formatDate(item['updated_on']) ?? '-', + style: _dataBold, + softWrap: true, + maxLines: 3, + ), + ), + Expanded( + flex: 2, + child: Text( + item['premium_amount'] ?? '-', + style: _dataBold, + softWrap: true, + maxLines: 3, + ), + ), + Expanded( + flex: 2, + child: Text( + item['payment_mode'] ?? '-', + style: _dataBold, + softWrap: true, + maxLines: 3, + ), + ), + Expanded( + flex: 2, + child: Text( + item['policy_number'] ?? '-', + style: _dataBold, + softWrap: true, + maxLines: 3, + ), + ), + + Expanded( + flex: 2, + child: Text(item['status'] ?? '-', style: _dataBold), + ), + + Expanded( + flex: 1, + child: Row( + children: [ + if (item['status'] == 'Awaiting Proposal') + Tooltip( + message: "Assign Staff", + child: IconButton( + icon: Icon(Icons.assignment_ind_outlined, size: 15), + onPressed: () { + handleStaff(context, item, item['id'], item['reg_no']); + }, + splashRadius: 5, + hoverColor: Colors.black12, + padding: const EdgeInsets.all(4), + constraints: const BoxConstraints(), + ), + ), + + Tooltip( + message: 'Edit', + child: IconButton( + icon: Image.asset( + "assets/miscellaneous/Edit.png", + height: 12, + width: 12, + ), + onPressed: () { + handleEdit(item); + }, + splashRadius: 5, + hoverColor: Colors.black12, + padding: const EdgeInsets.all(4), + constraints: const BoxConstraints(), + ), + ), + ], + ), + ), + ], + ), + ); + } + + Widget _buildDataCard(Map item, int sno) { + return Container( + margin: const EdgeInsets.symmetric(vertical: 6, horizontal: 8), + padding: const EdgeInsets.all(12), + decoration: BoxDecoration( + color: const Color(0xFFF6FEFD), + borderRadius: BorderRadius.circular(8.0), + border: Border.all(color: const Color(0xffD9EBE8)), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + /// Top row: Reg.No + Action menu + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text(item['reg_no'] ?? '-', style: _headerStyle), + PopupMenuButton( + color: Colors.white, + padding: EdgeInsets.zero, + offset: Offset(0, 30), + icon: Icon(Icons.more_vert, color: Color(0xFF475569), size: 14), + itemBuilder: (context) => [ + // CustomPopupMenuEntry( + // child: Container( + // padding: EdgeInsets.symmetric(horizontal: 8, vertical: 8), + // child: Column( + // mainAxisSize: MainAxisSize.min, + // children: _buildPopupMenuActions( + // context, + // item, + // item['id'], + // item['reg_no'], + // ), + // ), + // ), + // ), + ], + ), + ], + ), + const Divider(color: Color(0xffD9EBE8), thickness: 0.8), + + /// Company + Status + Row( + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text("Company", style: _cardheaderStyle), + Text(item['insurer_name'] ?? '-', style: _cardBodyStyle), + ], + ), + ), + const SizedBox(width: 5), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text("Status", style: _cardheaderStyle), + Text(item['status'] ?? '-', style: _cardBodyStyle), + ], + ), + ), + ], + ), + const SizedBox(height: 15), + + /// Agents + Assigned To + Row( + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text("Partner", style: _cardheaderStyle), + Text(item['agent_name'] ?? '-', style: _cardBodyStyle), + ], + ), + ), + const SizedBox(width: 5), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text("Assigned To", style: _cardheaderStyle), + Text( + item['assigned_to_name'] ?? '-', + style: _cardBodyStyle, + ), + ], + ), + ), + ], + ), + const SizedBox(height: 15), + + /// Insured Name + Premium + Row( + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text("Insured Name", style: _cardheaderStyle), + Text(item['insured_name'] ?? '-', style: _cardBodyStyle), + ], + ), + ), + const SizedBox(width: 5), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text("Premium", style: _cardheaderStyle), + Text(item['premium_amount'] ?? '-', style: _cardBodyStyle), + ], + ), + ), + ], + ), + const SizedBox(height: 15), + + /// Payment Mode + Policy Number + Row( + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text("Payment Mode", style: _cardheaderStyle), + Text(item['payment_mode'] ?? '-', style: _cardBodyStyle), + ], + ), + ), + const SizedBox(width: 5), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text("Policy Number", style: _cardheaderStyle), + Text(item['policy_number'] ?? '-', style: _cardBodyStyle), + ], + ), + ), + ], + ), + const SizedBox(height: 15), + + /// Date + Remarks + Row( + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text("Date", style: _cardheaderStyle), + Text( + _formatDate(item['updated_on']) ?? '-', + style: _cardBodyStyle, + ), + ], + ), + ), + const SizedBox(width: 5), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text("Remarks", style: _cardheaderStyle), + Text( + item['remarks'] ?? '-', + style: _cardBodyStyle, + maxLines: 3, + ), + ], + ), + ), + ], + ), + ], + ), + ); + } + + static final _dataBold = TextStyle( + fontSize: 14, + + fontWeight: FontWeight.w400, + color: Color(0xFF000000), + ); + static final _dataBoldsmall = TextStyle( + fontSize: 12, + + fontWeight: FontWeight.w400, + color: Color(0xFF000000), + ); + + static final _dataSub = TextStyle( + fontSize: 10, + fontWeight: FontWeight.w300, + color: Color(0xFF585757), + ); + + static const _headerStyle = TextStyle( + color: Colors.black, + fontWeight: FontWeight.bold, + ); + static const _cardheaderStyle = TextStyle( + color: Colors.black, + fontWeight: FontWeight.w600, + fontSize: 12, + ); + + static const _cardBodyStyle = TextStyle( + color: Color(0xFF545454), + fontWeight: FontWeight.w400, + fontSize: 12, + ); +} diff --git a/lib/presentation/screens/handler/inline b/lib/presentation/screens/handler/inline new file mode 100644 index 0000000..8c25b76 --- /dev/null +++ b/lib/presentation/screens/handler/inline @@ -0,0 +1,155 @@ +import 'package:flutter/material.dart'; + +class InlineEditableTable extends StatefulWidget { + const InlineEditableTable({super.key}); + + @override + State createState() => _InlineEditableTableState(); +} + +class _InlineEditableTableState extends State { + List> rows = [ + {"id": 1, "agent_name": "Ravi", "insurer_name": "ICICI"}, + {"id": 2, "agent_name": "Priya", "insurer_name": "HDFC"}, + ]; + + Map isEditingRow = {}; + Map agentControllers = {}; + Map insurerControllers = {}; + + void _addRow() { + final newId = DateTime.now().millisecondsSinceEpoch; + setState(() { + rows.add({"id": newId, "agent_name": "", "insurer_name": ""}); + isEditingRow[newId] = true; + agentControllers[newId] = TextEditingController(); + insurerControllers[newId] = TextEditingController(); + }); + } + + void _editRow(int id) { + setState(() { + isEditingRow[id] = true; + agentControllers[id] = TextEditingController( + text: rows.firstWhere((row) => row["id"] == id)["agent_name"], + ); + insurerControllers[id] = TextEditingController( + text: rows.firstWhere((row) => row["id"] == id)["insurer_name"], + ); + }); + } + + void _saveRow(int id) { + setState(() { + final index = rows.indexWhere((row) => row["id"] == id); + if (index != -1) { + rows[index]["agent_name"] = agentControllers[id]?.text ?? ""; + rows[index]["insurer_name"] = insurerControllers[id]?.text ?? ""; + } + isEditingRow[id] = false; + }); + } + + void _deleteRow(int id) { + setState(() { + rows.removeWhere((row) => row["id"] == id); + isEditingRow.remove(id); + agentControllers.remove(id); + insurerControllers.remove(id); + }); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar(title: const Text("Inline Editable Table")), + body: Padding( + padding: const EdgeInsets.all(16), + child: Column( + children: [ + Align( + alignment: Alignment.centerRight, + child: ElevatedButton.icon( + onPressed: _addRow, + icon: const Icon(Icons.add), + label: const Text("Add Row"), + ), + ), + const SizedBox(height: 10), + Expanded( + child: SingleChildScrollView( + scrollDirection: Axis.vertical, + child: DataTable( + columns: const [ + DataColumn(label: Text("Agent Name")), + DataColumn(label: Text("Insurer Name")), + DataColumn(label: Text("Actions")), + ], + rows: rows.map((row) { + final id = row["id"]; + final isEditing = isEditingRow[id] ?? false; + return DataRow( + cells: [ + DataCell( + isEditing + ? TextField( + controller: agentControllers[id], + decoration: const InputDecoration( + isDense: true, + border: OutlineInputBorder(), + ), + ) + : Text(row["agent_name"]), + ), + DataCell( + isEditing + ? TextField( + controller: insurerControllers[id], + decoration: const InputDecoration( + isDense: true, + border: OutlineInputBorder(), + ), + ) + : Text(row["insurer_name"]), + ), + DataCell( + Row( + children: [ + if (isEditing) + IconButton( + icon: const Icon( + Icons.save, + color: Colors.green, + ), + onPressed: () => _saveRow(id), + ) + else + IconButton( + icon: const Icon( + Icons.edit, + color: Colors.blue, + ), + onPressed: () => _editRow(id), + ), + IconButton( + icon: const Icon( + Icons.delete, + color: Colors.red, + ), + onPressed: () => _deleteRow(id), + ), + ], + ), + ), + ], + ); + }).toList(), + ), + ), + ), + ], + ), + ), + ); + } +} diff --git a/lib/presentation/screens/handler/new b/lib/presentation/screens/handler/new new file mode 100644 index 0000000..75cab87 --- /dev/null +++ b/lib/presentation/screens/handler/new @@ -0,0 +1,515 @@ +import 'dart:convert'; +import 'package:file_picker/file_picker.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:http/http.dart' as http; +import 'package:nhance_partner/presentation/providers/userRoleProvider.dart'; +import '../../../../core/config/env.dart'; +import '../../../../core/services/api_service.dart'; +import '../../../../data/services/auth_service.dart'; +import '../../../../data/utils/toastNotification.dart'; +import '../../layouts/responsive_layout.dart'; +import '../../providers/manager_provider.dart'; + +class QuotationPopUpTab extends ConsumerStatefulWidget { + String? id; + final Future Function()? onRefresh; + QuotationPopUpTab({super.key, this.id, this.onRefresh}); + + @override + ConsumerState createState() => QuotationTabState(); +} + +class QuotationTabState extends ConsumerState { + late ApiService apiService; + + bool isLoading = false; + bool blockKey = false; + + String? _token; + dynamic userId; + dynamic managerId; + dynamic role; + + List> getQuotationData = []; + List> originalData = []; + List> filteredData = []; + + @override + void initState() { + super.initState(); + apiService = ApiService(); + _initializeToken(); + Future.microtask(() { + managerId = ref.watch(managerIdProvider); + userId = ref.watch(userIdProvider); + role = ref.watch(userRoleProvider); + getQuotationList(); + }); + } + + Future _initializeToken() async { + _token = await AuthService.getToken(); + print("APISERTOKEN - $_token"); + } + + void refresh() { + getQuotationList(); + } + + /// ✅ Instead of using `widget.data`, call the API directly + Future getQuotationList() async { + print('Fetching quotation list from API...'); + setState(() => isLoading = true); + + try { + if (widget.id == null) { + throw Exception('QuotationTab: ID is null'); + } + + // Example: adjust this endpoint based on your actual API + final response = await apiService.findEnqQuotePolicyView(widget.id); + + // Extract quotation data safely + final quotations = (response["data"]?["quotations"] as List?) ?? []; + + final data = quotations + .map((e) => Map.from(e as Map)) + .toList(); + + setState(() { + getQuotationData = data; + filteredData = List.from(data); + blockKey = data.any((item) => item['status'] == 'Accepted'); + }); + + print('Fetched quotation data: ${data.length}'); + } catch (e, s) { + print('❌ Error fetching quotations: $e\n$s'); + } finally { + setState(() => isLoading = false); + } + } + + Future handleAction(String action, String quotationId) async { + final String apiUrldata = '${Env.apiUrl}quotation/acceptOrRejectQuotation'; + + if (_token == null) { + throw Exception('Token not found. Please log in.'); + } + + final Map data = { + "id": quotationId, + "status": action, + "action_by": userId, + "action_user": role, + }; + + print("data------- $data"); + + try { + final response = await http.post( + Uri.parse(apiUrldata), + headers: { + 'Authorization': 'Bearer $_token', + 'Content-Type': 'application/json', + 'app-signature': Env.App_Signature, + }, + body: jsonEncode(data), + ); + + if (response.statusCode == 200) { + print("Response: ${response.body}"); + ToastHelper.showSuccessToast(context, 'Status Updated'); + + // Close popup + Navigator.of(context).pop(); + + // Refresh parent + if (widget.onRefresh != null) { + await widget.onRefresh!(); + } + } else if (response.statusCode == 403) { + await apiService.clearLocalStorageAndRedirect(); + } else { + final responseBody = jsonDecode(response.body); + ToastHelper.showErrorToast(context, 'Status Updation Failed'); + print("Failed to submit. Status: ${response.statusCode}"); + print("Error: ${response.body}"); + } + } catch (e) { + print("Error submitting: $e"); + } + } + + void _showQuotationPopup() { + showDialog( + context: context, + builder: (BuildContext context) { + return Dialog( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(16), + ), + child: Container( + width: ResponsiveLayout.isMobile(context) + ? MediaQuery.of(context).size.width * 0.9 + : MediaQuery.of(context).size.width * 0.5, + padding: const EdgeInsets.all(24), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const Text( + 'Quotation Details', + style: TextStyle( + fontSize: 20, + fontWeight: FontWeight.bold, + ), + ), + IconButton( + icon: const Icon(Icons.close), + onPressed: () => Navigator.of(context).pop(), + ), + ], + ), + const SizedBox(height: 20), + Flexible( + child: SingleChildScrollView( + child: Column( + children: [ + ...filteredData + .map((item) => _buildQuotationCard(item)) + .toList(), + ], + ), + ), + ), + ], + ), + ), + ); + }, + ); + } + + Widget _buildQuotationCard(Map item) { + final bool isAccepted = item['status'] == 'Accepted'; + final bool isRejected = item['status'] == 'Rejected'; + final bool isPending = item['status'] == 'Pending'; + + return Container( + margin: const EdgeInsets.only(bottom: 16), + padding: const EdgeInsets.all(16), + decoration: BoxDecoration( + color: isAccepted + ? Colors.green.withOpacity(0.1) + : isRejected + ? Colors.red.withOpacity(0.1) + : const Color(0xFFF6FEFD), + borderRadius: BorderRadius.circular(12), + border: Border.all( + color: isAccepted + ? Colors.green + : isRejected + ? Colors.red + : const Color(0xffD9EBE8), + width: 1.5, + ), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // Header with status + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + item['insurer_name'] ?? '-', + style: const TextStyle( + fontSize: 16, + fontWeight: FontWeight.bold, + ), + ), + Container( + padding: const EdgeInsets.symmetric( + horizontal: 12, + vertical: 4, + ), + decoration: BoxDecoration( + color: _getStatusColor(item['status']), + borderRadius: BorderRadius.circular(12), + ), + child: Text( + item['status'] ?? '-', + style: const TextStyle( + color: Colors.white, + fontSize: 12, + fontWeight: FontWeight.w600, + ), + ), + ), + ], + ), + const SizedBox(height: 12), + + // Details + Row( + children: [ + Expanded( + child: _buildDetailItem( + 'IDV', + item['insured_declared_value']?.toString() ?? '-', + ), + ), + Expanded( + child: _buildDetailItem( + 'Premium', + item['premium_amount']?.toString() ?? '-', + ), + ), + ], + ), + const SizedBox(height: 8), + _buildDetailItem( + 'Plan Type', + item['insurance_plan_type']?.toString() ?? '-', + ), + + // Action buttons (only if pending and no other quotation is accepted) + if (isPending && !blockKey) ...[ + const SizedBox(height: 16), + Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + TextButton( + onPressed: () => handleAction('Rejected', item['id']), + style: TextButton.styleFrom( + padding: const EdgeInsets.symmetric( + horizontal: 24, + vertical: 12, + ), + backgroundColor: Colors.red.shade50, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + ), + ), + child: const Text( + 'Reject', + style: TextStyle( + color: Colors.red, + fontWeight: FontWeight.w600, + ), + ), + ), + const SizedBox(width: 12), + ElevatedButton( + onPressed: () => handleAction('Accepted', item['id']), + style: ElevatedButton.styleFrom( + padding: const EdgeInsets.symmetric( + horizontal: 24, + vertical: 12, + ), + backgroundColor: const Color(0xFF425B5B), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + ), + ), + child: const Text( + 'Accept', + style: TextStyle( + color: Colors.white, + fontWeight: FontWeight.w600, + ), + ), + ), + ], + ), + ], + + // Show message if blocked + if (isPending && blockKey) ...[ + const SizedBox(height: 12), + Container( + padding: const EdgeInsets.all(8), + decoration: BoxDecoration( + color: Colors.orange.shade50, + borderRadius: BorderRadius.circular(8), + ), + child: const Row( + children: [ + Icon(Icons.info_outline, size: 16, color: Colors.orange), + SizedBox(width: 8), + Expanded( + child: Text( + 'Another quotation has been accepted', + style: TextStyle(fontSize: 12, color: Colors.orange), + ), + ), + ], + ), + ), + ], + ], + ), + ); + } + + Widget _buildDetailItem(String label, String value) { + return Padding( + padding: const EdgeInsets.symmetric(vertical: 4), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + '$label: ', + style: const TextStyle( + fontSize: 14, + fontWeight: FontWeight.w600, + color: Color(0xFF545454), + ), + ), + Expanded( + child: Text( + value, + style: const TextStyle( + fontSize: 14, + fontWeight: FontWeight.w400, + color: Colors.black, + ), + ), + ), + ], + ), + ); + } + + Color _getStatusColor(String? status) { + switch (status) { + case 'Pending': + return Colors.orange; + case 'Accepted': + return Colors.green; + case 'Rejected': + return Colors.red; + default: + return Colors.grey; + } + } + + @override + Widget build(BuildContext context) { + return Container( + height: MediaQuery.of(context).size.height, + width: MediaQuery.of(context).size.width, + padding: const EdgeInsets.all(16), + child: isLoading + ? const Center(child: CircularProgressIndicator()) + : filteredData.isNotEmpty + ? Column( + children: [ + // Button to open popup + ElevatedButton.icon( + onPressed: _showQuotationPopup, + icon: const Icon(Icons.visibility), + label: Text('View Quotations (${filteredData.length})'), + style: ElevatedButton.styleFrom( + backgroundColor: const Color(0xFF425B5B), + foregroundColor: Colors.white, + padding: const EdgeInsets.symmetric( + horizontal: 24, + vertical: 12, + ), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + ), + ), + ), + const SizedBox(height: 20), + + // Summary cards + Expanded( + child: GridView.count( + crossAxisCount: ResponsiveLayout.isMobile(context) ? 1 : 3, + crossAxisSpacing: 16, + mainAxisSpacing: 16, + childAspectRatio: ResponsiveLayout.isMobile(context) + ? 3 + : 2, + children: [ + _buildSummaryCard( + 'Total Quotations', + filteredData.length.toString(), + Icons.description, + Colors.blue, + ), + _buildSummaryCard( + 'Accepted', + filteredData + .where((e) => e['status'] == 'Accepted') + .length + .toString(), + Icons.check_circle, + Colors.green, + ), + _buildSummaryCard( + 'Pending', + filteredData + .where((e) => e['status'] == 'Pending') + .length + .toString(), + Icons.pending, + Colors.orange, + ), + ], + ), + ), + ], + ) + : const Center(child: Text('No Available Data')), + ); + } + + Widget _buildSummaryCard( + String title, + String count, + IconData icon, + Color color, + ) { + return Container( + padding: const EdgeInsets.all(16), + decoration: BoxDecoration( + color: color.withOpacity(0.1), + borderRadius: BorderRadius.circular(12), + border: Border.all(color: color.withOpacity(0.3)), + ), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon(icon, size: 32, color: color), + const SizedBox(height: 8), + Text( + count, + style: TextStyle( + fontSize: 24, + fontWeight: FontWeight.bold, + color: color, + ), + ), + const SizedBox(height: 4), + Text( + title, + style: TextStyle( + fontSize: 14, + color: color, + fontWeight: FontWeight.w500, + ), + textAlign: TextAlign.center, + ), + ], + ), + ); + } +} diff --git a/lib/presentation/screens/handler/quotationPopUp.dart b/lib/presentation/screens/handler/quotationPopUp.dart new file mode 100644 index 0000000..6fa0d16 --- /dev/null +++ b/lib/presentation/screens/handler/quotationPopUp.dart @@ -0,0 +1,582 @@ +import 'dart:convert'; +import 'package:file_picker/file_picker.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:http/http.dart' as http; +import 'package:nhance_partner/presentation/providers/userRoleProvider.dart'; +import '../../../../core/config/env.dart'; +import '../../../../core/services/api_service.dart'; +import '../../../../data/services/auth_service.dart'; +import '../../../../data/utils/toastNotification.dart'; +import '../../layouts/responsive_layout.dart'; +import '../../providers/manager_provider.dart'; + +class QuotationPopUpTab extends ConsumerStatefulWidget { + String? id; + final Future Function()? onRefresh; + QuotationPopUpTab({super.key, this.id, this.onRefresh}); + + @override + ConsumerState createState() => QuotationTabState(); +} + +class QuotationTabState extends ConsumerState { + late ApiService apiService; + + bool isLoading = false; + bool blockKey = false; + + String? _token; + dynamic userId; + dynamic managerId; + dynamic role; + + List> getQuotationData = []; + List> originalData = []; + List> filteredData = []; + + @override + void initState() { + super.initState(); + apiService = ApiService(); + _initializeToken(); + Future.microtask(() { + managerId = ref.watch(managerIdProvider); + userId = ref.watch(userIdProvider); + role = ref.watch(userRoleProvider); + getQuotationList(); + }); + } + + Future _initializeToken() async { + _token = await AuthService.getToken(); + print("APISERTOKEN - $_token"); + } + + void refresh() { + getQuotationList(); + } + + /// ✅ Instead of using `widget.data`, call the API directly + Future getQuotationList() async { + print('Fetching quotation list from API...'); + setState(() => isLoading = true); + + try { + if (widget.id == null) { + throw Exception('QuotationTab: ID is null'); + } + + // Example: adjust this endpoint based on your actual API + final response = await apiService.findEnqQuotePolicyView(widget.id); + + // Extract quotation data safely + final quotations = (response["data"]?["quotations"] as List?) ?? []; + + final data = quotations + .map((e) => Map.from(e as Map)) + .toList(); + + setState(() { + getQuotationData = data; + filteredData = List.from(data); + blockKey = data.any((item) => item['status'] == 'Accepted'); + }); + + print('Fetched quotation data: ${data.length}'); + } catch (e, s) { + print('❌ Error fetching quotations: $e\n$s'); + } finally { + setState(() => isLoading = false); + } + } + + Future handleAction(String action, String quotationId) async { + final String apiUrldata = '${Env.apiUrl}quotation/acceptOrRejectQuotation'; + + if (_token == null) { + throw Exception('Token not found. Please log in.'); + } + + final Map data = { + "id": quotationId, + "status": action, + "action_by": userId, + "action_user": role, + }; + + print("data------- $data"); + + try { + final response = await http.post( + Uri.parse(apiUrldata), + headers: { + 'Authorization': 'Bearer $_token', + 'Content-Type': 'application/json', + 'app-signature': Env.App_Signature, + }, + body: jsonEncode(data), + ); + + if (response.statusCode == 200) { + print("Response: ${response.body}"); + ToastHelper.showSuccessToast(context, 'Status Updated'); + + // Close popup + Navigator.of(context).pop(); + + // Refresh parent + if (widget.onRefresh != null) { + await widget.onRefresh!(); + } + } else if (response.statusCode == 403) { + await apiService.clearLocalStorageAndRedirect(); + } else { + final responseBody = jsonDecode(response.body); + ToastHelper.showErrorToast(context, 'Status Updation Failed'); + print("Failed to submit. Status: ${response.statusCode}"); + print("Error: ${response.body}"); + } + } catch (e) { + print("Error submitting: $e"); + } + } + + Widget _buildQuotationPopupContent() { + return Container( + // width: ResponsiveLayout.isMobile(context) + // ? MediaQuery.of(context).size.width * 0.9 + // : MediaQuery.of(context).size.width * 0.5, + padding: const EdgeInsets.all(12), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(16), + // border: Border.all(color: const Color(0xFFD9EBE8)), + // boxShadow: [ + // BoxShadow( + // color: Colors.black.withOpacity(0.05), + // blurRadius: 8, + // offset: const Offset(0, 4), + // ), + // ], + ), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const Text( + 'Quotation Details', + style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold), + ), + + IconButton( + onPressed: () { + Navigator.pop(context); + }, + icon: Icon(Icons.close), + ), + ], + ), + const SizedBox(height: 5), + Flexible( + child: SingleChildScrollView( + child: _buildQuotationTable(), // ✅ Only one table + ), + ), + ], + ), + ); + } + + Widget _buildQuotationTable() { + return Container( + padding: const EdgeInsets.all(16), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(12), + border: Border.all(color: const Color(0xFFD9EBE8)), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // 🔹 Table Header + Container( + padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 8), + color: const Color(0xFFEFF6F5), + child: Row( + children: const [ + Expanded( + flex: 1, + child: Text( + "IDV", + style: TextStyle(fontWeight: FontWeight.bold), + ), + ), + Expanded( + flex: 2, + child: Text( + "Plan Type", + style: TextStyle(fontWeight: FontWeight.bold), + ), + ), + Expanded( + flex: 1, + child: Text( + "Premium", + style: TextStyle(fontWeight: FontWeight.bold), + ), + ), + Expanded( + flex: 3, + child: Center( + child: Text( + "Action", + style: TextStyle(fontWeight: FontWeight.bold), + ), + ), + ), + ], + ), + ), + const Divider(height: 1, color: Color(0xFFD9EBE8)), + + // 🔹 Table Rows + ...filteredData.map((item) { + final bool isAccepted = item['status'] == 'Accepted'; + final bool isRejected = item['status'] == 'Rejected'; + final bool isPending = item['status'] == 'Pending'; + + return Container( + padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 8), + decoration: BoxDecoration( + border: Border( + bottom: BorderSide(color: Colors.grey.withOpacity(0.2)), + ), + color: isAccepted + ? Colors.green.withOpacity(0.05) + : isRejected + ? Colors.red.withOpacity(0.05) + : Colors.white, + ), + child: Row( + children: [ + Expanded( + flex: 1, + child: Text( + item['insured_declared_value']?.toString() ?? '-', + style: const TextStyle(fontSize: 14), + ), + ), + Expanded( + flex: 2, + child: Text( + item['insurance_plan_type']?.toString() ?? '-', + style: const TextStyle(fontSize: 14), + ), + ), + Expanded( + flex: 1, + child: Text( + item['premium_amount']?.toString() ?? '-', + style: const TextStyle(fontSize: 14), + ), + ), + Expanded( + flex: 3, + child: Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + if (isPending && !blockKey) ...[ + TextButton( + onPressed: () => + handleAction('Rejected', item['id']), + style: TextButton.styleFrom( + backgroundColor: Colors.red.shade50, + padding: const EdgeInsets.symmetric( + horizontal: 12, + ), + ), + child: const Text( + 'Reject', + style: TextStyle( + color: Colors.red, + fontWeight: FontWeight.w600, + ), + ), + ), + const SizedBox(width: 8), + ElevatedButton( + onPressed: () => + handleAction('Accepted', item['id']), + style: ElevatedButton.styleFrom( + backgroundColor: const Color(0xFF425B5B), + padding: const EdgeInsets.symmetric( + horizontal: 12, + ), + ), + child: const Text( + 'Accept', + style: TextStyle( + color: Colors.white, + fontWeight: FontWeight.w600, + ), + ), + ), + ], + if (isAccepted) + const Text( + 'Accepted', + style: TextStyle( + color: Colors.green, + fontWeight: FontWeight.bold, + ), + ), + if (isRejected) + const Text( + 'Rejected', + style: TextStyle( + color: Colors.red, + fontWeight: FontWeight.bold, + ), + ), + ], + ), + ), + ], + ), + ); + }), + ], + ), + ); + } + + Widget _buildQuotationCard(Map item) { + final bool isAccepted = item['status'] == 'Accepted'; + final bool isRejected = item['status'] == 'Rejected'; + final bool isPending = item['status'] == 'Pending'; + + return Container( + margin: const EdgeInsets.only(bottom: 16), + padding: const EdgeInsets.all(16), + decoration: BoxDecoration( + color: isAccepted + ? Colors.green.withOpacity(0.1) + : isRejected + ? Colors.red.withOpacity(0.1) + : const Color(0xFFF6FEFD), + borderRadius: BorderRadius.circular(12), + border: Border.all( + color: isAccepted + ? Colors.green + : isRejected + ? Colors.red + : const Color(0xffD9EBE8), + width: 1.5, + ), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // Header with status + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + item['insurer_name'] ?? '-', + style: const TextStyle( + fontSize: 16, + fontWeight: FontWeight.bold, + ), + ), + Container( + padding: const EdgeInsets.symmetric( + horizontal: 12, + vertical: 4, + ), + decoration: BoxDecoration( + color: _getStatusColor(item['status']), + borderRadius: BorderRadius.circular(12), + ), + child: Text( + item['status'] ?? '-', + style: const TextStyle( + color: Colors.white, + fontSize: 12, + fontWeight: FontWeight.w600, + ), + ), + ), + ], + ), + const SizedBox(height: 12), + + // Details + Row( + children: [ + Expanded( + child: _buildDetailItem( + 'IDV', + item['insured_declared_value']?.toString() ?? '-', + ), + ), + Expanded( + child: _buildDetailItem( + 'Premium', + item['premium_amount']?.toString() ?? '-', + ), + ), + ], + ), + const SizedBox(height: 8), + _buildDetailItem( + 'Plan Type', + item['insurance_plan_type']?.toString() ?? '-', + ), + + // Action buttons (only if pending and no other quotation is accepted) + if (isPending && !blockKey) ...[ + const SizedBox(height: 16), + Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + TextButton( + onPressed: () => handleAction('Rejected', item['id']), + style: TextButton.styleFrom( + padding: const EdgeInsets.symmetric( + horizontal: 24, + vertical: 12, + ), + backgroundColor: Colors.red.shade50, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + ), + ), + child: const Text( + 'Reject', + style: TextStyle( + color: Colors.red, + fontWeight: FontWeight.w600, + ), + ), + ), + const SizedBox(width: 12), + ElevatedButton( + onPressed: () => handleAction('Accepted', item['id']), + style: ElevatedButton.styleFrom( + padding: const EdgeInsets.symmetric( + horizontal: 24, + vertical: 12, + ), + backgroundColor: const Color(0xFF425B5B), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + ), + ), + child: const Text( + 'Accept', + style: TextStyle( + color: Colors.white, + fontWeight: FontWeight.w600, + ), + ), + ), + ], + ), + ], + + // Show message if blocked + if (isPending && blockKey) ...[ + const SizedBox(height: 12), + Container( + padding: const EdgeInsets.all(8), + decoration: BoxDecoration( + color: Colors.orange.shade50, + borderRadius: BorderRadius.circular(8), + ), + child: const Row( + children: [ + Icon(Icons.info_outline, size: 16, color: Colors.orange), + SizedBox(width: 8), + Expanded( + child: Text( + 'Another quotation has been accepted', + style: TextStyle(fontSize: 12, color: Colors.orange), + ), + ), + ], + ), + ), + ], + ], + ), + ); + } + + Widget _buildDetailItem(String label, String value) { + return Padding( + padding: const EdgeInsets.symmetric(vertical: 4), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + '$label: ', + style: const TextStyle( + fontSize: 14, + fontWeight: FontWeight.w600, + color: Color(0xFF545454), + ), + ), + Expanded( + child: Text( + value, + style: const TextStyle( + fontSize: 14, + fontWeight: FontWeight.w400, + color: Colors.black, + ), + ), + ), + ], + ), + ); + } + + Color _getStatusColor(String? status) { + switch (status) { + case 'Pending': + return Colors.orange; + case 'Accepted': + return Colors.green; + case 'Rejected': + return Colors.red; + default: + return Colors.grey; + } + } + + @override + @override + Widget build(BuildContext context) { + return Container( + // height: MediaQuery.of(context).size.height, + // width: MediaQuery.of(context).size.width, + // padding: const EdgeInsets.all(16), + child: isLoading + ? const Center(child: CircularProgressIndicator()) + : filteredData.isNotEmpty + ? SingleChildScrollView( + child: Column( + children: [ + _buildQuotationPopupContent(), // ✅ now renders inline + const SizedBox(height: 20), + ], + ), + ) + : const Center(child: Text('No Available Data')), + ); + } +} diff --git a/lib/presentation/screens/login/login_screen.dart b/lib/presentation/screens/login/login_screen.dart index c9c1876..b8f75e9 100644 --- a/lib/presentation/screens/login/login_screen.dart +++ b/lib/presentation/screens/login/login_screen.dart @@ -263,9 +263,9 @@ class _LoginScreenState extends ConsumerState { final message = data['data']['message']; - // setState(() { - // enteredEmailOrMobile = true; - // }); + setState(() { + enteredEmailOrMobile = true; + }); if (verification == true) { // if (switcherStatus == 1) { diff --git a/lib/presentation/screens/staff/Enquiry/enquiry_inline_list.dart b/lib/presentation/screens/staff/Enquiry/enquiry_inline_list.dart new file mode 100644 index 0000000..f25fab0 --- /dev/null +++ b/lib/presentation/screens/staff/Enquiry/enquiry_inline_list.dart @@ -0,0 +1,2904 @@ +import 'package:file_picker/file_picker.dart'; +import 'package:flutter/services.dart'; +import 'package:http/http.dart' as http; +import 'package:dropdown_search/dropdown_search.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:go_router/go_router.dart'; +import 'package:google_fonts/google_fonts.dart'; +import 'package:intl/intl.dart'; +import 'package:nhance_partner/presentation/screens/handler/quotationPopUp.dart'; +import 'package:nhance_partner/presentation/screens/staff/Enquiry/tabs/tab.dart'; +import 'package:shared_preferences/shared_preferences.dart'; + +import '../../../../core/config/env.dart'; +import '../../../../core/routing/routes.dart'; + +import '../../../../core/services/api_service.dart'; +import 'package:universal_html/html.dart' as html; + +import '../../../../data/services/auth_service.dart'; +import '../../../../data/utils/Pagination.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/quotation_staff_proivder.dart'; +import '../../../providers/userRoleProvider.dart'; +import '../../../themes/indicators/customizd_file_upload.dart'; +import '../../../themes/indicators/export_btn.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/text_field_theme_inline_editor.dart' + hide UpperCaseTextFormatter; +import '../../../widgets/custom_Stdate_EnDate_Filter.dart'; +import '../assignStaff.dart'; + +class EnquiryListStaffInline extends ConsumerStatefulWidget { + const EnquiryListStaffInline({super.key}); + @override + ConsumerState createState() => EnquiryStaffState(); +} + +class EnquiryStaffState extends ConsumerState { + int currentPage = 1; + int itemsPerPage = 10; + late ApiService apiService; + dynamic userId; + dynamic idPrimary; + dynamic managerId; + dynamic dashboardKey; + dynamic SelectedStatus; + dynamic SelectedStaffId; + dynamic handlerId; + String? _token; + final _formKey = GlobalKey(); + Map fieldErrors = {}; + + // List> dataVal = []; + List> getStaffData = []; + List> originalData = []; + List> filteredData = []; + bool isLoading = false; + dynamic roleId; + bool _showFilterRow = false; + + Map controllers = {}; + List tabHeader = [ + 'startDate', + 'endDate', + 'name', + 'email', + 'mobile', + 'code', + 'address', + 'regNo', + 'remarks', + ]; + + Map isEditingRow = {}; + Map> rowControllers = {}; + + //---------------------- Enquiry TAB Initializations Starts -------------------------------- // + + List> getVehicleTypeData = []; + List> filteredVechicleData = []; + + List> getAgentListData = []; + List> filteredAgentData = []; + + List> getInsurersData = []; + List> filteredInsurersData = []; + + String? selectedVehicleType; + String? selectedInsurer; + String? selectedAgent; + PlatformFile? docUploadedRCFile; + PlatformFile? docUploadedIDProof; + PlatformFile? docUploadedPrevPolicy; + + String? selectedRCFile; + String? selectedIdProof; + String? selectedPrevPolicy; + + String? selectedRCFileName; + + String? rcFileUrlFromApi; + String? idProofFileUrlFromApi; + String? prevPolicyFileUrlFromApi; + + String? selectedId; + + Map dataDetails() { + final data = { + "agent_id": ((roleId == 'handler') || (roleId == 'manager')) + ? selectedAgent + : userId, + "name": controllers["name"]?.text, + "mobile": controllers["mobile"]?.text, + "email": controllers["email"]?.text, + "reg_no": controllers["regNo"]?.text, + "vehicle_type_id": selectedVehicleType, + "is_data_created_by_handler": roleId == 'handler' ? '1' : '0', + "is_data_created_by_manager": roleId == 'manager' ? '1' : '0', + // "insurer_id": selectedInsurer, + // "rc_file_name": "rc_doc.pdf", + // "id_proof_file_name": "id_proof.pdf", + // "previous_policy_file_name": "previous_policy.pdf", + "remarks": controllers["remarks"]?.text, + + "assigned_to": selectedStaff, + "insurer_id": selectedInsurer, + "broker_id": selectedBroker, + "manager_id": managerId, + "created_by": userId, + }; + + return data; + } + + final GlobalKey>> dropDownKey = + GlobalKey>>(); + final GlobalKey>> + dropDownKeyInsurer = GlobalKey>>(); + final GlobalKey>> dropDownKeyAgent = + GlobalKey>>(); + //---------------------- Assign Staff starts -------------------------------- // + + final GlobalKey>> + dropDownKeyInsurerEnqAsgn = + GlobalKey>>(); + + final GlobalKey>> dropDownKeyBroker = + GlobalKey>>(); + final GlobalKey>> + dropDownSelectStaffKey = + GlobalKey>>(); + + List> getStaffDetailsDataEnqAsgn = []; + List> filteredStaffDataEnqAsgn = []; + + String? selectedStaff; + String? selectedRegNum; + String? selectedStaffName; + List> getBrokerData = []; + List> filteredBrokerData = []; + String? selectedBroker; + + //---------------------- Assign Staff Ends -------------------------------- // + //---------------------- Enquiry TAB Initializations Ends -------------------------------- // + + @override + void initState() { + super.initState(); + + apiService = ApiService(); + + for (String field in tabHeader) { + controllers[field] = TextEditingController(); + } + _initializeToken(); + + Future.microtask(() async { + // final id = ref.read(managerIdProvider); + roleId = ref.read(userRoleProvider); + userId = ref.read(userIdProvider); + managerId = ref.read(managerIdProvider); + + final prefs = await SharedPreferences.getInstance(); + dashboardKey = prefs.getString('dashboardKeyProvider'); + final dashboardStatus = prefs.getString('dashboardStatusProvider'); + final dashboardStaffId = prefs.getString('dashboardStaffIdProvider'); + + print('handlerIdENQ - $handlerId'); + print("C72 => r : $roleId | uId: $userId !mID : $managerId "); + + print('dashboardKey - $dashboardKey'); + print('dashboardStatus - $dashboardStatus'); + print('dashboardStaffId - $dashboardStaffId'); + + if (managerId != null && + dashboardKey != null && + dashboardStatus != null && + dashboardStaffId != null) { + print("DASHBOARD STATus-$dashboardStatus -- $dashboardStaffId -"); + SelectedStatus = dashboardStatus; + SelectedStaffId = dashboardStaffId; + getStaffList( + managerId, + roleId, + SelectedStatus: dashboardStatus, + SelectedStaffId: dashboardStaffId, + ); + } else { + print('ELSE'); + getStaffList(managerId, roleId); + } + if (managerId != null) { + print('managerId - $managerId'); + getAgentList(managerId); + } + + final userID = ref.watch(userIdProvider); + + print("managerId - $managerId"); + if (userID != null) { + print('hansles'); + getStaffDetailsForEnquiryAssignment(userID); + } + }); + + getVehicleType(); + getInsurers(); + getBroker(); + } + + Future _initializeToken() async { + _token = await AuthService.getToken(); + print("APISERTOKEN - $_token"); + } + + void refresh() { + final id = ref.read(managerIdProvider); + final roleId = ref.read(userRoleProvider); + final userId = ref.read(userIdProvider); + + print("C82 => r : $roleId | mId: $id | uId: $userId "); + if (managerId != null) { + getStaffList(managerId, roleId); + } + } + + void filterDateRange() { + // ✅ Call your API + getStaffList( + managerId, + roleId, + SelectedStatus: SelectedStatus ?? '', + SelectedStaffId: SelectedStaffId ?? '', + ); + } + + Future refrshfilterDateRange() async { + print('refrshfilterDateRange'); + // setState(() async { + final prefs = await SharedPreferences.getInstance(); + + // ✅ Clear any old data + await prefs.remove('dashboardKeyProvider'); + await prefs.remove('dashboardStatusProvider'); + await prefs.remove('dashboardStaffIdProvider'); + SelectedStatus = ''; + SelectedStaffId = null; + controllers['startDate']!.clear(); + controllers['endDate']!.clear(); + + controllers['startDate']?.text = ''; + controllers['endDate']?.text = ''; + // Reset the FormField validation + _formKey.currentState?.reset(); + // }); + + final int parsedManagerId = int.tryParse(managerId.toString()) ?? 0; + print('refrshfilterDateRange 1'); + getStaffList( + // managerId, + parsedManagerId, + roleId, + SelectedStatus: SelectedStatus ?? '', + SelectedStaffId: SelectedStaffId ?? '', + ); + print('refrshfilterDateRange 2'); + } + + Future getInsurers() async { + print('Insurers called'); + setState(() { + isLoading = true; + }); + + try { + final response = await apiService.fetchMasterDropDown('Insurers'); + + if (response['status'] == 200) { + print('getInsurers - ${response['data']}'); + setState(() { + getInsurersData = List>.from(response['data']); + print('API Data - $getInsurersData'); + + filteredInsurersData = List.from(getInsurersData); + print('originalData - $filteredInsurersData'); + }); + } else { + getInsurersData = []; + filteredInsurersData = []; + } + } catch (e) { + print('Exception occurred: $e'); + } finally { + setState(() { + isLoading = false; + }); + } + } + + Future getBroker() async { + print('getBroker called'); + setState(() { + isLoading = true; + }); + + try { + final response = await apiService.fetchMasterDropDown('Broker'); + + if (response['status'] == 200) { + print('getBroker - ${response['data']}'); + setState(() { + getBrokerData = List>.from(response['data']); + print('API Data - $getBrokerData'); + + filteredBrokerData = List.from(getBrokerData); + print('originalData - $filteredBrokerData'); + }); + } else { + getBrokerData = []; + filteredBrokerData = []; + } + } catch (e) { + print('Exception occurred: $e'); + } finally { + setState(() { + isLoading = false; + }); + } + } + + Future getStaffDetailsForEnquiryAssignment(int id) async { + print('getStaffDetailsForEnquiryAssignment called By handler'); + setState(() { + isLoading = true; + }); + + try { + // final response = await apiService.fetchStaffUserList(id, role); + final response = await apiService.fetchStaffListForEnquiryAssignDropDown( + id, + roleId, + ); + + if (response['status'] == 'success') { + print('getStaffDetailsForEnquiryAssignment - ${response['data']}'); + setState(() { + getStaffDetailsDataEnqAsgn = List>.from( + response['data'], + ); + print('API Data - $getStaffDetailsDataEnqAsgn'); + + filteredStaffDataEnqAsgn = List.from(getStaffDetailsDataEnqAsgn); + print('originalData - $filteredStaffDataEnqAsgn'); + }); + } else { + getStaffDetailsDataEnqAsgn = []; + filteredStaffDataEnqAsgn = []; + } + } catch (e) { + print('Exception occurred: $e'); + } finally { + setState(() { + isLoading = false; + }); + } + } + + Future getStaffList( + int managerId, + role, { + String fromDate = '', + String toDate = '', + String SelectedStatus = '', + String SelectedStaffId = '', + }) async { + print('A72 => Fns called => $managerId | $role'); + setState(() { + isLoading = true; + }); + + dynamic fromDt; + dynamic toDt; + + final fromDateVal = controllers['startDate']?.text ?? ''; + final toDateVal = controllers['endDate']?.text ?? ''; + final dateFormat = DateFormat('dd-MM-yyyy'); + + if (dashboardKey != 'fromDashboard' && + fromDateVal == '' && + toDateVal == '') { + print("ENQ LIST PAGE"); + + final today = DateTime.now(); + fromDt = dateFormat.format(today.subtract(Duration(days: 15))); + toDt = dateFormat.format(today); + + print('Enq FROM TO - $fromDt - $toDt'); + } else { + fromDt = fromDateVal; + toDt = toDateVal; + } + + try { + final response = await apiService.fetchEnquiryList( + managerId, + role, + fromDate: fromDt, + toDate: toDt, + selectedStatus: SelectedStatus != null ? SelectedStatus : '', + selectedStaffId: SelectedStaffId ?? '', + ); + + if (response['status'] == 'success') { + final data = response['data']; + final fromDate = response['from_date'] ?? ''; + final toDate = response['to_date'] ?? ''; + + print('FromDate : $fromDate'); + print('ToDate : $toDate'); + + setState(() { + controllers['startDate']?.text = fromDate; + controllers['endDate']?.text = toDate; + if (data is List) { + getStaffData = List>.from(data); + } else if (data is Map) { + getStaffData = [Map.from(data)]; + } else { + getStaffData = []; + } + originalData = getStaffData; + filteredData = List.from(originalData); + }); + } else { + getStaffData = []; + originalData = []; + } + } catch (e) { + print('Exception occurred: $e'); + } finally { + setState(() { + isLoading = false; + }); + } + } + + List get _paginatedData { + // Sort descending by id first + final sortedData = [...filteredData] + ..sort((a, b) => int.parse(b['id']) - int.parse(a['id'])); + + if (sortedData.isEmpty) return []; + + // Ensure currentPage is valid + final maxPage = (sortedData.length / itemsPerPage).ceil(); + final safePage = currentPage.clamp(1, maxPage); + + final startIndex = (safePage - 1) * itemsPerPage; + final endIndex = (startIndex + itemsPerPage).clamp(0, sortedData.length); + + return sortedData.sublist(startIndex, endIndex); + } + + void filterData(String query) { + print("FilterDAta - $query"); + setState(() { + filteredData = getStaffData.where((item) { + // final isActiveStatus = item['is_active'] == "1" ? "active" : "inactive"; + + return (item['agent_name'] ?? '-').toLowerCase().contains( + query.toLowerCase(), + ) || + (item['reg_no'] ?? '-').toLowerCase().contains( + query.toLowerCase(), + ) || + (item['insurer_name'] ?? '-').toLowerCase().contains( + query.toLowerCase(), + ) || + (item['assigned_to_name'] ?? '-').toLowerCase().contains( + query.toLowerCase(), + ) || + (item['insured_name'] ?? '-').toLowerCase().contains( + query.toLowerCase(), + ) || + (item['premium_amount'] ?? '-').toLowerCase().contains( + query.toLowerCase(), + ) || + (item['payment_mode'] ?? '-').toLowerCase().contains( + query.toLowerCase(), + ) || + (_formatDate(item['created_on']) ?? '-').toLowerCase().contains( + query.toLowerCase(), + ) || + (_formatDate(item['updated_on']) ?? '-').toLowerCase().contains( + query.toLowerCase(), + ) || + (item['policy_number'] ?? '-').toLowerCase().contains( + query.toLowerCase(), + ) || + (item['status'] ?? '-').toLowerCase().contains(query.toLowerCase()); + }).toList(); + }); + } + + final TextEditingController _searchStaffController = TextEditingController(); + + String _formatDate(String rawDate) { + try { + final dateTime = DateTime.parse(rawDate); + return DateFormat( + 'dd-MM-yyyy HH:mm', + ).format(dateTime); // 24-hour format + } catch (e) { + return rawDate; // fallback if parsing fails + } + } + + Future handleStaff( + BuildContext context, + dynamic data, + id, + regNum, + ) async { + showDialog( + context: context, + builder: (ctx) => AssignStaffDialog( + enquiryPrimaryId: id, + regNum: regNum, + userId: userId, + onSubmit: (value) { + debugPrint("New assignY: $value"); + refresh(); + }, + ), + ); + } + + Future handleProposalAccept(BuildContext context, String id) async { + final result = showDialog( + context: context, + barrierDismissible: true, // allows tapping outside to close + builder: (ctx) { + return Dialog( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(26), + ), + insetPadding: const EdgeInsets.all(24), + child: Container( + width: 500, + height: 300, + color: Colors.white, + // width: MediaQuery.of(context).size.width * 0.4, + // height: MediaQuery.of(context).size.height * 0.4, + // width: MediaQuery.of(context).size.width * 0.7, + // height: MediaQuery.of(context).size.height * 0.8, + child: QuotationPopUpTab(id: id, onRefresh: refrshfilterDateRange), + ), + ); + }, + ); + + print("Dialog result: $result"); + (); + } + + void handleEdit(Map item) { + print("handleEdit"); + + final id = int.tryParse(item['id'].toString()) ?? 0; + idPrimary = item['id']; + + print("handleEdit - $idPrimary"); + setState(() { + selectedId = idPrimary; + isEditingRow[id] = true; + }); + for (final field in tabHeader) { + // Only set if controller exists + if (controllers.containsKey(field)) { + controllers[field]!.text = item[field] != null + ? item[field].toString() + : ''; // fallback to empty string + } + } + + //dropdowns or IDs + selectedVehicleType = item['vehicle_type_id']; + selectedAgent = item['agent_id']; + managerId = item['manager_id']; + controllers["regNo"]?.text = item['reg_no']; + controllers["remarks"]?.text = item['remarks']; + + setState(() {}); + + // setState(() { + // isEditingRow[id] = true; + // rowControllers[id] = { + // 'agent_name': TextEditingController(text: item['agent_name'] ?? ''), + // 'insurer_name': TextEditingController(text: item['insurer_name'] ?? ''), + // 'reg_no': TextEditingController(text: item['reg_no'] ?? ''), + // 'premium_amount': TextEditingController( + // text: item['premium_amount'] ?? '', + // ), + // 'payment_mode': TextEditingController(text: item['payment_mode'] ?? ''), + // }; + // }); + } + + void handleSave(int id) { + // final controllers = rowControllers[id]; + // if (controllers == null) return; + if (!_validateRequiredFields()) return; + setState(() { + // final index = getStaffData.indexWhere( + // (e) => e['id'].toString() == id.toString(), + // ); + // if (index != -1) { + // getStaffData[index] = { + // ...getStaffData[index], + // 'agent_name': controllers['agent_name']?.text, + // 'insurer_name': controllers['insurer_name']?.text, + // 'reg_no': controllers['reg_no']?.text, + // 'premium_amount': controllers['premium_amount']?.text, + // 'payment_mode': controllers['payment_mode']?.text, + // }; + // } + // + // if (index != -1) { + // print("EDITField - ${getStaffData[index]}"); + // getStaffData[index] = { + // ...getStaffData[index], + // 'agent_id': selectedAgent, + // "name": controllers["name"]?.text, + // "mobile": controllers["mobile"]?.text, + // "email": controllers["email"]?.text, + // "reg_no": controllers["regNo"]?.text, + // "vehicle_type_id": selectedVehicleType, + // }; + // } + + isEditingRow[id] = false; + rowControllers.remove(id); + }); + handleEnqTabSave(); + } + + void handleCancel(int id) { + setState(() { + // Check if this is a new unsaved row + final row = getStaffData.firstWhere( + (item) => item['id'].toString() == id.toString(), + orElse: () => {}, + ); + + // If policy_number is empty, it's a new row - remove it + if (row.isNotEmpty && + (row['policy_number'] == null || + row['policy_number'] == '' || + row['policy_number'] == '-')) { + getStaffData.removeWhere( + (item) => item['id'].toString() == id.toString(), + ); + originalData.removeWhere( + (item) => item['id'].toString() == id.toString(), + ); + filteredData.removeWhere( + (item) => item['id'].toString() == id.toString(), + ); + rowControllers.remove(id); + } + + // Turn off editing mode + isEditingRow.remove(id); + + // Clear idPrimary if it matches + if (idPrimary.toString() == id.toString()) { + idPrimary = null; + } + }); + } + + //-------------------------------Assign Logics staff start ---------------------------// + + Widget buildSelectStaffMem(ctx) { + Map? selectedVehicle; + try { + selectedVehicle = filteredStaffDataEnqAsgn.firstWhere( + (item) => item['id'].toString() == selectedStaff, + ); + } catch (e) { + selectedVehicle = null; // ✅ fallback + } + + return SizedBox( + height: 35, + width: MediaQuery.of(context).size.width * 0.08, + child: DropdownSearch>( + key: dropDownSelectStaffKey, + // selectedItem: selectedVehicle.isNotEmpty ? selectedVehicle : null, + selectedItem: selectedVehicle, + items: (filter, infiniteScrollProps) { + return filteredStaffDataEnqAsgn; + }, + + itemAsString: (val) => val['name'].toString(), + compareFn: (item, selectedItem) => + item['id'] == selectedItem['id'], // ✅ compare by id + validator: (val) { + if (val == null) { + return "Required"; // ✅ error message + } + return null; + }, + decoratorProps: DropDownDecoratorProps( + decoration: + AppInputDecorations.dropdownDecoration( + label: "Select Staff Member", + ).copyWith( + filled: true, + fillColor: Colors.white, // 👈 makes the dropdown input white + isDense: true, + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(5), + borderSide: const BorderSide(color: Colors.black, width: 0.1), + ), + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(5), + borderSide: const BorderSide(color: Colors.black, width: 0.1), + ), + contentPadding: EdgeInsets.symmetric( + horizontal: 2, + vertical: 6, + ), + ), + ), + dropdownBuilder: (context, selectedItem) => Align( + alignment: Alignment.centerLeft, + child: Text( + selectedItem != null ? selectedItem['name'].toString() : "", + style: GoogleFonts.poppins(fontSize: 11, color: Colors.black), + overflow: TextOverflow.ellipsis, + maxLines: 1, + softWrap: false, + ), + ), + popupProps: PopupProps.menu( + fit: FlexFit.loose, + constraints: BoxConstraints(maxHeight: 250), + menuProps: MenuProps( + backgroundColor: + Colors.white, // 👈 sets dropdown background to white + ), + showSearchBox: true, + searchFieldProps: TextFieldProps( + decoration: InputDecoration( + filled: true, + fillColor: Colors.white, + hintText: "Search Staff ...", + hintStyle: GoogleFonts.inter(fontSize: 12, color: Colors.black), + enabledBorder: OutlineInputBorder( + borderSide: BorderSide(color: Colors.white), // 👈 Normal border + ), + focusedBorder: OutlineInputBorder( + borderSide: BorderSide( + color: Colors.white, + width: 1.5, + ), // 👈 Focused border + ), + ), + ), + // constraints: BoxConstraints(), + itemBuilder: (context, item, isDisabled, isSelected) { + return Container( + // color: isSelected ? Colors.blue.withOpacity(0.1) : null, + padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 3), + child: Text( + item['name'].toString(), + style: GoogleFonts.inter(fontSize: 12, color: Colors.black), + ), + ); + }, + ), + + onChanged: (val) { + if (val != null) { + print("Selected Staff : ${val['name']}"); + print("Id: ${val['id']}"); + selectedStaffName = val['name']; + selectedStaff = val['id']; + // controllers['agentId']?.text = val['agent_code']; + // agentId = agent['id']; + } + }, + ), + ); + } + + Widget buildInsurer(BuildContext context) { + Map? selectedInsurerd = filteredInsurersData.firstWhere( + (item) => item['id'].toString() == selectedInsurer, + orElse: () => {}, + ); + return Container( + height: 35, + width: MediaQuery.of(context).size.width * 0.08, + child: DropdownSearch>( + key: dropDownKeyInsurerEnqAsgn, + selectedItem: selectedInsurerd.isNotEmpty ? selectedInsurerd : null, + items: (filter, infiniteScrollProps) { + return filteredInsurersData; + }, + + itemAsString: (val) => val['name'].toString(), // what to show + compareFn: (item, selectedItem) => + item['id'] == selectedItem['id'], // ✅ compare by id + validator: (val) { + if (val == null) { + return "Required"; // ✅ error message + } + return null; + }, + + dropdownBuilder: (context, selectedItem) => Align( + alignment: Alignment.centerLeft, + child: Text( + selectedItem != null ? selectedItem['name'].toString() : "", + style: GoogleFonts.poppins(fontSize: 11, color: Colors.black), + overflow: TextOverflow.ellipsis, + maxLines: 1, + softWrap: false, + ), + ), + decoratorProps: DropDownDecoratorProps( + decoration: + AppInputDecorations.dropdownDecoration( + label: "Select Insurer", + ).copyWith( + filled: true, + fillColor: Colors.white, // 👈 makes the dropdown input white + isDense: true, + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(5), + borderSide: const BorderSide(color: Colors.black, width: 0.1), + ), + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(5), + borderSide: const BorderSide(color: Colors.black, width: 0.1), + ), + contentPadding: EdgeInsets.symmetric( + horizontal: 2, + vertical: 6, + ), + ), + ), + + popupProps: PopupProps.menu( + fit: FlexFit.loose, + constraints: BoxConstraints(maxHeight: 250), + menuProps: MenuProps( + backgroundColor: + Colors.white, // 👈 sets dropdown background to white + ), + showSearchBox: true, + searchFieldProps: TextFieldProps( + decoration: InputDecoration( + filled: true, + fillColor: Colors.white, + hintText: "Search Insurer...", + hintStyle: GoogleFonts.inter(fontSize: 12, color: Colors.black), + enabledBorder: OutlineInputBorder( + borderSide: BorderSide(color: Colors.white), // 👈 Normal border + ), + focusedBorder: OutlineInputBorder( + borderSide: BorderSide( + color: Colors.white, + width: 1.5, + ), // 👈 Focused border + ), + ), + ), + itemBuilder: (context, item, isDisabled, isSelected) { + return Container( + // color: isSelected ? Colors.blue.withOpacity(0.1) : null, + padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 3), + child: Text( + item['name'].toString(), + style: GoogleFonts.inter(fontSize: 12, color: Colors.black), + ), + ); + }, + // constraints: BoxConstraints(), + ), + + onChanged: (val) { + if (val != null) { + print("Selected Insurer : ${val['name']}"); + print("Id: ${val['id']}"); + selectedInsurer = val['id']; + // controllers['agentId']?.text = val['agent_code']; + // agentId = agent['id']; + } + }, + ), + ); + } + + Widget buildBroker(BuildContext context) { + Map? selectedBrokers = filteredBrokerData.firstWhere( + (item) => item['id'].toString() == selectedBroker, + orElse: () => {}, + ); + return SizedBox( + height: 35, + width: MediaQuery.of(context).size.width * 0.07, + child: DropdownSearch>( + key: dropDownKeyBroker, + selectedItem: selectedBrokers.isNotEmpty ? selectedBrokers : null, + items: (filter, infiniteScrollProps) { + return filteredBrokerData; + }, + + itemAsString: (val) => val['name'].toString(), // what to show + compareFn: (item, selectedItem) => + item['id'] == selectedItem['id'], // ✅ compare by id + validator: (val) { + if (val == null) { + return "Required"; // ✅ error message + } + return null; + }, + dropdownBuilder: (context, selectedItem) => Align( + alignment: Alignment.centerLeft, + child: Text( + selectedItem != null ? selectedItem['name'].toString() : "", + style: GoogleFonts.poppins(fontSize: 11, color: Colors.black), + overflow: TextOverflow.ellipsis, + maxLines: 1, + softWrap: false, + ), + ), + decoratorProps: DropDownDecoratorProps( + decoration: + AppInputDecorations.dropdownDecoration( + label: "Select Broker", + ).copyWith( + filled: true, + fillColor: Colors.white, // 👈 makes the dropdown input white + isDense: true, + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(5), + borderSide: const BorderSide(color: Colors.black, width: 0.1), + ), + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(5), + borderSide: const BorderSide(color: Colors.black, width: 0.1), + ), + contentPadding: EdgeInsets.symmetric( + horizontal: 2, + vertical: 6, + ), + ), + ), + + popupProps: PopupProps.menu( + fit: FlexFit.loose, + constraints: BoxConstraints(maxHeight: 250), + menuProps: MenuProps( + backgroundColor: + Colors.white, // 👈 sets dropdown background to white + ), + showSearchBox: true, + searchFieldProps: TextFieldProps( + decoration: InputDecoration( + filled: true, + fillColor: Colors.white, + hintText: "Search Broker...", + hintStyle: GoogleFonts.inter(fontSize: 12, color: Colors.black), + enabledBorder: OutlineInputBorder( + borderSide: BorderSide(color: Colors.white), // 👈 Normal border + ), + focusedBorder: OutlineInputBorder( + borderSide: BorderSide( + color: Colors.white, + width: 1.5, + ), // 👈 Focused border + ), + ), + ), + + itemBuilder: (context, item, isDisabled, isSelected) { + return Container( + // color: isSelected ? Colors.blue.withOpacity(0.1) : null, + padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 3), + child: Text( + item['name'].toString(), + style: GoogleFonts.inter(fontSize: 12, color: Colors.black), + ), + ); + }, + // constraints: BoxConstraints(), + ), + + onChanged: (val) { + if (val != null) { + print("Selected Broker : ${val['name']}"); + print("Id: ${val['id']}"); + selectedBroker = val['id']; + // controllers['agentId']?.text = val['agent_code']; + // agentId = agent['id']; + } + }, + ), + ); + } + + // -------------------------------Assign Logics staff end---------------------------// + + //---------------------- Enquiry TAB Logins Starts -------------------------------- // + + Map dataEnquiryTabDetails() { + final data = { + "agent_id": ((roleId == 'handler') || (roleId == 'manager')) + ? selectedAgent + : userId, + "name": controllers["name"]?.text, + "mobile": controllers["mobile"]?.text, + "email": controllers["email"]?.text, + "reg_no": controllers["regNo"]?.text, + "vehicle_type_id": selectedVehicleType, + "is_data_created_by_handler": roleId == 'handler' ? '1' : '0', + "is_data_created_by_manager": roleId == 'manager' ? '1' : '0', + // "insurer_id": selectedInsurer, + // "rc_file_name": "rc_doc.pdf", + // "id_proof_file_name": "id_proof.pdf", + // "previous_policy_file_name": "previous_policy.pdf", + "remarks": controllers["remarks"]?.text, + "manager_id": managerId, + "created_by": userId, + }; + + return data; + } + + Future getVehicleType() async { + print('getClaimList called'); + setState(() { + isLoading = true; + }); + + try { + final response = await apiService.fetchMasterDropDown('vehicleType'); + + if (response['status'] == 200) { + print('getVehicleTypeData - ${response['data']}'); + setState(() { + getVehicleTypeData = List>.from( + response['data'], + ); + print('API Data - $getVehicleTypeData'); + + filteredVechicleData = List.from(getVehicleTypeData); + print('originalData - $filteredVechicleData'); + }); + } else { + getVehicleTypeData = []; + filteredVechicleData = []; + } + } catch (e) { + print('Exception occurred: $e'); + } finally { + setState(() { + isLoading = false; + }); + } + } + + Future getAgentList(id) async { + print('getAgentListData called'); + setState(() { + isLoading = true; + }); + + try { + final response = await apiService.fetchAgentNameDropDown(id); + print('getAgentListData called response'); + print('get Agent- ${response['data']}'); + if (response['status'] == 'success') { + print('get Agent- ${response['data']}'); + setState(() { + getAgentListData = List>.from(response['data']); + print('API Data - $getAgentListData'); + + filteredAgentData = List.from(getAgentListData); + print('originalAgentData - $filteredAgentData'); + }); + } else { + getAgentListData = []; + filteredAgentData = []; + } + } catch (e) { + print('Exception occurred: $e'); + } finally { + setState(() { + isLoading = false; + }); + } + } + + Widget buildAgentName(BuildContext context) { + Map? selectedAgntName = filteredAgentData.firstWhere( + (item) => item['id'].toString() == selectedAgent, + orElse: () => {}, + ); + return SizedBox( + width: MediaQuery.of(context).size.width * 0.08, + height: 35, + child: DropdownSearch>( + key: dropDownKeyAgent, + selectedItem: selectedAgntName.isNotEmpty ? selectedAgntName : null, + items: (filter, infiniteScrollProps) { + return filteredAgentData; + }, + + itemAsString: (val) => val['name'].toString(), // what to show + compareFn: (item, selectedItem) => + item['id'] == selectedItem['id'], // ✅ compare by id + validator: (val) { + if (val == null) { + return "Required"; // ✅ error message + } + return null; + }, + dropdownBuilder: (context, selectedItem) => Align( + alignment: Alignment.centerLeft, + child: Text( + selectedItem != null + ? selectedItem['name'].toString() + : "", // ✅ FIXED + style: GoogleFonts.poppins(fontSize: 11, color: Colors.black), + overflow: TextOverflow.ellipsis, + maxLines: 1, + softWrap: false, + ), + ), + decoratorProps: DropDownDecoratorProps( + decoration: + AppInputDecorations.dropdownDecoration( + label: "Select Partner", + ).copyWith( + filled: true, + fillColor: Colors.white, // 👈 makes the dropdown input white + isDense: true, + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(5), + borderSide: const BorderSide(color: Colors.black, width: 0.1), + ), + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(5), + borderSide: const BorderSide(color: Colors.black, width: 0.1), + ), + contentPadding: EdgeInsets.symmetric( + horizontal: 1, + // vertical: 6, + ), + ), + ), + + popupProps: PopupProps.menu( + fit: FlexFit.loose, + constraints: BoxConstraints(maxHeight: 250), + menuProps: MenuProps( + backgroundColor: + Colors.white, // 👈 sets dropdown background to white + ), + showSearchBox: true, + + searchFieldProps: TextFieldProps( + decoration: InputDecoration( + filled: true, + fillColor: Colors.white, + hintText: "Search Partner...", + hintStyle: GoogleFonts.inter(fontSize: 12, color: Colors.black), + enabledBorder: OutlineInputBorder( + borderSide: BorderSide(color: Colors.white), // 👈 Normal border + ), + focusedBorder: OutlineInputBorder( + borderSide: BorderSide( + color: Colors.white, + width: 1.5, + ), // 👈 Focused border + ), + ), + ), + + // constraints: BoxConstraints(), + itemBuilder: (context, item, isDisabled, isSelected) { + return Container( + // color: isSelected ? Colors.blue.withOpacity(0.1) : null, + padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 3), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Text( + item['name'].toString(), + style: GoogleFonts.inter(fontSize: 12, color: Colors.black), + ), + Text( + item['agent_code'].toString(), + style: GoogleFonts.inter(fontSize: 11, color: Colors.grey), + ), + ], + ), + ); + }, + ), + + onChanged: (val) { + if (val != null) { + print("Selected Partner : ${val['name']}"); + print("Id: ${val['id']}"); + selectedAgent = val['id']; + // controllers['agentId']?.text = val['agent_code']; + // agentId = agent['id']; + } + }, + ), + ); + } + + Widget buildVehicleNumber(BuildContext context) { + return ThemedFormInlineField( + controller: controllers['regNo']!, + borderColor: Colors.black, + isdense: true, + inputFormatters: [ + UpperCaseTextFormatter(), // 👈 custom formatter for uppercase + FilteringTextInputFormatter.allow(RegExp(r'[A-Za-z0-9- ]')), + ], + validator: (value) => Validators.requiredVechileNum(value, "regNo"), + widthNone: true, + ); + } + + Widget buildInsuredName(BuildContext context) { + return ThemedFormInlineField( + controller: controllers['name']!, + // errorText: fieldErrors['name'], + borderColor: Colors.black, + isdense: true, + validator: (value) => Validators.requiredField(value, "name"), + widthNone: true, + ); + } + + Widget buildVehicleType(BuildContext context) { + // Find the matching map from your list + + Map? selectedVehicle = filteredVechicleData.firstWhere( + (item) => item['id'].toString() == selectedVehicleType, + orElse: () => {}, + ); + + return SizedBox( + width: MediaQuery.of(context).size.width * 0.07, + height: 35, + child: DropdownSearch>( + key: dropDownKey, + selectedItem: selectedVehicle.isNotEmpty ? selectedVehicle : null, + items: (filter, infiniteScrollProps) { + return filteredVechicleData; + }, + validator: (val) { + if (val == null) { + return "Required"; // ✅ error message + } + return null; + }, + + itemAsString: (val) => val['vehicle_type'].toString(), + compareFn: (item, selectedItem) => + item['id'] == selectedItem['id'], // ✅ compare by id + + dropdownBuilder: (context, selectedItem) => Align( + alignment: Alignment.centerLeft, + child: Text( + selectedItem != null + ? selectedItem['vehicle_type'].toString() + : "", // ✅ FIXED + style: GoogleFonts.poppins(fontSize: 11, color: Colors.black), + overflow: TextOverflow.ellipsis, + maxLines: 1, + softWrap: false, + ), + ), + decoratorProps: DropDownDecoratorProps( + decoration: + AppInputDecorations.dropdownDecoration( + label: "Select Vehicle Type", + ).copyWith( + filled: true, + fillColor: Colors.white, // 👈 makes the dropdown input white + isDense: true, + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(5), + borderSide: const BorderSide(color: Colors.black, width: 0.1), + ), + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(5), + borderSide: const BorderSide(color: Colors.black, width: 0.1), + ), + contentPadding: EdgeInsets.symmetric( + horizontal: 8, + vertical: 6, + ), + ), + ), + + popupProps: PopupProps.menu( + fit: FlexFit.loose, + constraints: BoxConstraints(maxHeight: 200), + menuProps: MenuProps( + backgroundColor: + Colors.white, // 👈 sets dropdown background to white + ), + showSearchBox: true, + searchFieldProps: TextFieldProps( + decoration: InputDecoration( + filled: true, + fillColor: Colors.white, + hintText: "Search Vehicle Type...", + hintStyle: GoogleFonts.inter(fontSize: 12, color: Colors.black), + enabledBorder: OutlineInputBorder( + borderSide: BorderSide(color: Colors.white), // 👈 Normal border + ), + focusedBorder: OutlineInputBorder( + borderSide: BorderSide( + color: Colors.white, + width: 1.5, + ), // 👈 Focused border + ), + ), + ), + + // constraints: BoxConstraints(), + itemBuilder: (context, item, isDisabled, isSelected) { + return Container( + // color: isSelected ? Colors.blue.withOpacity(0.1) : null, + padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 3), + child: Text( + item['vehicle_type'].toString(), + style: GoogleFonts.inter(fontSize: 12, color: Colors.black), + ), + ); + }, + ), + + onChanged: (val) { + if (val != null) { + print("Selected vehicle_type : ${val['vehicle_type']}"); + print("Id: ${val['id']}"); + selectedVehicleType = val['id']; + // controllers['agentId']?.text = val['agent_code']; + // agentId = agent['id']; + } + }, + ), + ); + } + + Widget buildEmail(BuildContext context) { + return ThemedFormInlineField( + controller: controllers['email']!, + borderColor: Colors.black, + isdense: true, + validator: (value) => Validators.nonReqemail(value, "email"), + inputFormatters: [ + FilteringTextInputFormatter.allow(RegExp(r'[a-zA-Z0-9_@.]')), + ], + txtwidth: ResponsiveLayout.isMobile(context) + ? null + : MediaQuery.of(context).size.width * 0.26, + ); + } + + Widget buildPhNumber(BuildContext context) { + return ThemedFormInlineField( + controller: controllers['mobile']!, + borderColor: Colors.black, + isdense: true, + validator: (value) => Validators.nonReqphone(value, "phone"), + inputFormatters: [FilteringTextInputFormatter.allow(RegExp(r'[ 0-9]'))], + txtwidth: ResponsiveLayout.isMobile(context) + ? null + : MediaQuery.of(context).size.width * 0.26, + ); + } + + Widget buildRemarks(BuildContext context) { + return ThemedFormField( + // maxLength: 500, + enableBorderWidth: 0.1, + controller: controllers['remarks']!, + borderColor: Colors.black, + verticalPad: 8, + horizonalPad: 10, + isdense: true, + // keyboardType: TextInputType.multiline, + txtwidth: ResponsiveLayout.isMobile(context) + ? null + : MediaQuery.of(context).size.width * 0.1, + ); + } + + Widget buildResponsiveUploadField({ + required String label, + required String? hintText, + required double? width, + required void Function(String fileName, dynamic file) onFileSelected, + bool showDownload = false, + VoidCallback? onRemove, + VoidCallback? onDownload, + }) { + final isMobile = ResponsiveLayout.isMobile(context); + + final uploadWidget = ThemedUploadField( + hintText: hintText ?? "Upload Document", + txtwidth: width, + padVertical: 6, + padHorizontal: 8, + + backgroundColor: Color(0xffD9EBE8), + txtName: label, + isTxtBtnCase: true, + onFileSelected: onFileSelected, + ); + + return Column( + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [uploadWidget], + ), + ], + ); + } + + Widget buildUploadRCDocument(BuildContext context) { + return buildResponsiveUploadField( + label: "RC", + width: 60, + hintText: selectedRCFile, + onFileSelected: (fileName, file) { + setState(() { + docUploadedRCFile = file; + selectedRCFileName = fileName; + }); + ToastHelper.showSuccessToast(context, 'File Uploaded Successfully'); + }, + showDownload: docUploadedRCFile != null || rcFileUrlFromApi != null, + onRemove: () { + setState(() { + docUploadedRCFile = null; + rcFileUrlFromApi = null; + selectedRCFileName = null; + }); + }, + onDownload: () => apiService.downloadFile( + // apiUrl: 'api/agent/downloadAgentCertificateFile?agent_id=$selectedId', + apiUrl: + 'api/enquiry/downloadEnquiryFile?enquiry_id=$selectedId&file_type=rc', + + apiId: selectedId.toString(), + + localFile: docUploadedRCFile, + fileName: 'RC', + ), + ); + } + + Widget buildUploadIDDocument(BuildContext context) { + return buildResponsiveUploadField( + label: "ID Proof", + width: 80, + hintText: selectedIdProof, + onFileSelected: (fileName, file) { + setState(() { + docUploadedIDProof = file; + // selectedFileNames = fileName; + }); + + ToastHelper.showSuccessToast(context, 'File Uploaded Successfully'); + }, + showDownload: docUploadedIDProof != null || idProofFileUrlFromApi != null, + onRemove: () { + setState(() { + docUploadedIDProof = null; + idProofFileUrlFromApi = null; + // selectedFileNames = null; + }); + }, + onDownload: () => apiService.downloadFile( + // apiUrl: 'api/agent/downloadAgentCertificateFile?agent_id=$selectedId', + apiUrl: + 'api/enquiry/downloadEnquiryFile?enquiry_id=$selectedId&file_type=id_proof', + + apiId: selectedId.toString(), + localFile: docUploadedIDProof, + fileName: 'Id_Proof', + ), + ); + } + + Widget buildUploadPolicyDocument(BuildContext context) { + return buildResponsiveUploadField( + width: 100, + label: "PrevPolicy", + hintText: selectedPrevPolicy, + onFileSelected: (fileName, file) { + setState(() { + docUploadedPrevPolicy = file; + // selectedFileNames = fileName; + }); + ToastHelper.showSuccessToast(context, 'File Uploaded Successfully'); + }, + showDownload: + docUploadedPrevPolicy != null || prevPolicyFileUrlFromApi != null, + onRemove: () { + setState(() { + docUploadedPrevPolicy = null; + prevPolicyFileUrlFromApi = null; + // selectedFileNames = null; + }); + }, + onDownload: () => apiService.downloadFile( + // apiUrl: 'api/agent/downloadAgentCertificateFile?agent_id=$selectedId', + apiUrl: + 'api/enquiry/downloadEnquiryFile?enquiry_id=$selectedId&file_type=previous_policy', + + apiId: selectedId.toString(), + localFile: docUploadedPrevPolicy, + fileName: 'Previous_Policy', + ), + ); + } + + Widget buildDocRC(BuildContext context, selectedEnquiryId) { + return InkWell( + onTap: () { + print("Upload $selectedEnquiryId"); + final selectedId = selectedEnquiryId; + // final path = + // 'api/agent/downloadAgentIncentiveFile?id=$selectedId'; + final path = + 'api/enquiry/downloadEnquiryFile?enquiry_id=$selectedId&file_type=rc'; + print("Uploadpath $path"); + apiService.getPdfDownload(path, selectedId); + }, + child: Container( + padding: const EdgeInsets.symmetric(vertical: 4, horizontal: 6), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(5), + color: const Color(0xFF425B5B), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Tooltip( + message: 'Download RC', + child: Text( + "RC", + style: GoogleFonts.inter( + fontSize: 12, + fontWeight: FontWeight.w500, + color: Colors.white, + ), + ), + ), + ], + ), + ), + ); + } + + Widget buildDocIdProof(BuildContext context, selectedEnquiryId) { + return InkWell( + onTap: () { + print("Upload $selectedEnquiryId"); + final selectedId = selectedEnquiryId; + // final path = + // 'api/agent/downloadAgentIncentiveFile?id=$selectedId'; + final path = + 'api/enquiry/downloadEnquiryFile?enquiry_id=$selectedId&file_type=id_proof'; + print("Uploadpath $path"); + apiService.getPdfDownload(path, selectedId); + }, + child: Container( + padding: const EdgeInsets.symmetric(vertical: 4, horizontal: 6), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(5), + color: const Color(0xFF425B5B), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Tooltip( + message: 'Download ID Proof', + child: Text( + "ID Proof", + style: GoogleFonts.inter( + fontSize: 12, + fontWeight: FontWeight.w500, + color: Colors.white, + ), + ), + ), + ], + ), + ), + ); + } + + Widget buildDocPrevPolicy(BuildContext context, selectedEnquiryId) { + return InkWell( + onTap: () { + print("QD597 Upload $selectedEnquiryId"); + final selectedId = selectedEnquiryId; + // final path = + // 'api/agent/downloadAgentIncentiveFile?id=$selectedId'; + final path = + 'api/enquiry/downloadEnquiryFile?enquiry_id=$selectedId&file_type=previous_policy'; + + print("Uploadpath $path"); + apiService.getPdfDownload(path, selectedId); + }, + child: Container( + padding: const EdgeInsets.symmetric(vertical: 4, horizontal: 6), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(5), + color: const Color(0xFF425B5B), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Tooltip( + message: 'Download Previous Policy', + child: Text( + "Prev Policy", + style: GoogleFonts.inter( + fontSize: 12, + fontWeight: FontWeight.w500, + color: Colors.white, + ), + ), + ), + ], + ), + ), + ); + } + + // Update validation method to set errors + bool _validateRequiredFields() { + setState(() { + fieldErrors.clear(); // Clear previous errors + + // Validate Partner/Agent + if (selectedAgent == null || selectedAgent == '') { + fieldErrors['agent'] = 'Partner is required'; + } + + // Validate Insured Name + if (controllers['name']?.text.trim().isEmpty ?? true) { + fieldErrors['name'] = 'Insured Name is required'; + } + + // Validate Vehicle Number + if (controllers['regNo']?.text.trim().isEmpty ?? true) { + fieldErrors['regNo'] = 'Vehicle Number is required'; + } + + // Validate Vehicle Type + if (selectedVehicleType == null || selectedVehicleType == '') { + fieldErrors['vehicleType'] = 'Vehicle Type is required'; + } + + // Validate email format if provided + final email = controllers['email']?.text.trim() ?? ''; + if (email.isNotEmpty) { + final emailRegex = RegExp(r'^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$'); + if (!emailRegex.hasMatch(email)) { + fieldErrors['email'] = 'Invalid email format'; + } + } + + // Validate mobile number if provided + final mobile = controllers['mobile']?.text.trim() ?? ''; + if (mobile.isNotEmpty) { + final cleanMobile = mobile.replaceAll(' ', ''); + if (cleanMobile.length < 10) { + fieldErrors['mobile'] = 'Must be at least 10 digits'; + } + } + }); + + return true; + + return fieldErrors.isEmpty; + } + + Future handleEnqTabSave() async { + if (!_validateRequiredFields()) return; + + // setState(() { + // isSaving = true; // start saving + // }); + + final dataSet = dataDetails(); + + try { + await createUserData(dataSet); // API call + // ToastHelper.showInfoToast(context, "Enquiry saved successfully"); + } catch (e) { + showDialog( + context: context, + builder: (_) => AlertDialog( + title: const Text('Error'), + content: Text(e.toString()), + actions: [ + TextButton( + onPressed: () => Navigator.of(context).pop(), + child: const Text('OK'), + ), + ], + ), + ); + } finally { + // setState(() => isSaving = false); // re-enable button after save + } + } + + Future attachFiles(http.MultipartRequest request) async { + Future addFileOrKeepName( + PlatformFile? file, + String? apiFileName, + String fieldName, + ) async { + if (file != null) { + // User uploaded a new file → send as multipart + if (file.bytes != null) { + request.files.add( + http.MultipartFile.fromBytes( + fieldName, + file.bytes!, + filename: file.name, + ), + ); + } else if (file.path != null) { + request.files.add( + await http.MultipartFile.fromPath( + fieldName, + file.path!, + filename: file.name, + ), + ); + } + print("📎 Attached new file → $fieldName"); + } else if (apiFileName != null && apiFileName.isNotEmpty) { + // No new upload → tell backend to keep old file + request.fields[fieldName] = apiFileName; + print("🔗 Kept old file → $fieldName = $apiFileName"); + } else { + // Nothing at all + request.fields[fieldName] = ""; + } + } + + await addFileOrKeepName( + docUploadedRCFile, + rcFileUrlFromApi, + 'rc_file_name', + ); + await addFileOrKeepName( + docUploadedIDProof, + idProofFileUrlFromApi, + 'id_proof_file_name', + ); + await addFileOrKeepName( + docUploadedPrevPolicy, + prevPolicyFileUrlFromApi, + 'previous_policy_file_name', + ); + } + + Future createUserData(Map userData) async { + final bool isNewRow = + idPrimary != null && + int.tryParse(idPrimary.toString()) != null && + int.parse(idPrimary.toString()) > 1000000000000; + + final bool isUpdating = + idPrimary != null && idPrimary != 'null' && !isNewRow; + + print('isNewRow: $isNewRow, isUpdating: $isUpdating'); + + // final bool isUpdating = idPrimary != 'null'; + final id = idPrimary; + print('UPDId- $id'); + + // final bool isUpdating = false; + final uri = Uri.parse( + isUpdating + ? '${Env.apiUrl}enquiry/updateEnquiry' + : '${Env.apiUrl}enquiry/createEnquiry', + ); + if (_token == null) { + throw Exception('Token not found. Please log in.'); + } + + // Use MultipartRequest (POST only) + final request = http.MultipartRequest('POST', uri); + request.headers['Authorization'] = 'Bearer $_token'; + request.headers['app-signature'] = Env.App_Signature; + + // If updating, spoof the method Laravel-style + if (isUpdating) { + request.fields['id'] = id!; + request.fields['updated_by'] = userId!.toString(); + } else { + request.fields['created_by'] = userId!.toString(); + } + + print("USerDAta - $userData"); + + // userData.forEach((key, value) { + // request.fields[key] = value.toString(); + // print("✅ Encoded travel_details2: ${request.fields[key]}"); + // }); + await attachFiles(request); + userData.forEach((key, value) { + if (key != 'rc_file_name' && key != 'id_proof_file_name') { + request.fields[key] = value.toString(); + print("✅ Encoded $key: ${request.fields[key]}"); + } else { + print('Something Missing..'); + } + }); + + // attach files + + // request.fields['agent_id'] = selectedId.toString(); + + print(" Sending request with fields: ${request.fields}"); + + try { + final streamedResponse = await request.send(); + final response = await http.Response.fromStream(streamedResponse); + print("Response status: ${response.statusCode}"); + print("Response body: ${response.body}"); + + if (response.statusCode == 200 || response.statusCode == 201) { + // dispose(); + print("✅ Enquiry submitted successfully!"); + ToastHelper.showSuccessToast(context, 'Saved Enquiry'); + print("Response: ${response.body}"); + // Clear the temporary ID + idPrimary = null; + refrshfilterDateRange(); + // getStaffList(managerId, roleId); + // setState(() => isSaving = false); + } else if (response.statusCode == 403) { + await apiService.clearLocalStorageAndRedirect(); + } else { + print("❌ Submission failed. Status: ${response.statusCode}"); + print("Body: ${response.body}"); + + // setState(() => isSaving = false); + showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + title: Text("Enquiry Creation Failed"), + content: Text( + "There was a problem in creating enquiry. Please try again.", + ), + actions: [ + TextButton( + child: Text("OK"), + onPressed: () { + Navigator.of(context).pop(); + }, + ), + ], + ); + }, + ); + } + } catch (e) { + print("🔥 Error submitting user: $e"); + } + } + + //---------------------- Enquiry TAB Logins End -------------------------------- // + @override + Widget build(BuildContext context) { + return MainLayout( + title: "Enquiries", + body: Container( + // color: Colors.yellow.shade50, + width: MediaQuery.of(context).size.width, + padding: EdgeInsets.all(3.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + // Container( + // height: 30, + // width: MediaQuery.of(context).size.width, + // child: GestureDetector( + // onTap: () { + // context.go(AppRoutes.dashboard); + // }, + // child: Row( + // crossAxisAlignment: CrossAxisAlignment.center, + // mainAxisAlignment: MainAxisAlignment.start, + // children: [ + // Tooltip( + // message: 'Back', + // child: IconButton( + // icon: const Icon(Icons.arrow_left_sharp, size: 25), + // onPressed: () async { + // context.go(AppRoutes.dashboard); + // final prefs = await SharedPreferences.getInstance(); + // await prefs.remove('dashboardKeyProvider'); + // await prefs.remove('dashboardStatusProvider'); + // await prefs.remove('dashboardStaffIdProvider'); + // }, + // splashRadius: 18, + // padding: const EdgeInsets.all(4), + // hoverColor: Colors.black12, + // constraints: const BoxConstraints(), + // ), + // ), + // const SizedBox(width: 5), // spacing between icon and text + // Text( + // "Enquiries", + // style: TextStyle( + // fontSize: 18, + // fontWeight: FontWeight.w600, + // ), + // ), + // ], + // ), + // ), + // ), + // SizedBox(height: 5), + ResponsiveLayout.isMobile(context) + ? Container( + height: MediaQuery.of(context).size.height * 0.69, + child: SingleChildScrollView( + child: Padding( + padding: EdgeInsets.all(2), + child: _buildContent(context), + ), + ), + ) + : Expanded( + child: Container( + width: MediaQuery.of(context).size.width, + // padding: EdgeInsets.symmetric( + // horizontal: 8.0, + // vertical: 2.0, + // ), + child: _buildContent(context), + ), + ), + + Container( + // height: 20, + width: MediaQuery.of(context).size.width, + // color: Colors.green.shade50, + child: PaginationControls( + currentPage: currentPage, + itemsPerPage: itemsPerPage, + // totalItems: dataVal.length, + totalItems: filteredData.length, + // activeColor: layoutColor, // your theme color + onPageChanged: (page) { + setState(() { + currentPage = page; + }); + }, + onItemsPerPageChanged: (items) { + setState(() { + itemsPerPage = items; + currentPage = 1; + }); + }, + ), + ), + ], + ), + ), + ); + } + + Future buildStatusActions(BuildContext context, status, id) async { + print("Actionsstatus - $status -$id"); + dynamic val; + if (status == 'Awaiting Proposal' || status == 'Proposal Created') { + val = ''; + } else { + val = 'Policy'; + } + + final result = await showDialog( + context: context, + barrierDismissible: + false, // optional - prevents closing by tapping outside + builder: (context) => TabEnquiryStaffList(showKey: val), + ); + + // ✅ Code here runs *after* the dialog is closed + print("Dialog closed"); + print("Dialog result: $result"); + refrshfilterDateRange(); + } + + Widget _buildContent(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // if (ResponsiveLayout.isMobile(context)) ...[ + // Container( + // padding: EdgeInsets.all(8.0), + // color: Color(0xffD9EBE8), + // child: DateFilterRow( + // key: ValueKey(SelectedStatus ?? ''), + // onFilterStaff: (val) { + // print('Selected Filterd STAFF Id - $val'); + // SelectedStaffId = val; + // }, + // selectedStaffId: SelectedStaffId, + // role: roleId, + // id: userId, + // selectedStatusVal: SelectedStatus, + // startController: controllers['startDate']!, + // endController: controllers['endDate']!, + // formKey: _formKey, + // isMobile: ResponsiveLayout.isMobile(context), + // onStatusChanged: (val) { + // SelectedStatus = val; // update parent + // }, + // + // onFilter: () { + // // call your filter logic + // filterDateRange(); + // }, + // onRefresh: () { + // // call your refresh logic + // refrshfilterDateRange(); + // }, + // ), + // ), + // ], + Container( + // height: 40, + // color: Colors.pink, + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Container( + // height: 30, + // width: MediaQuery.of(context).size.width, + child: GestureDetector( + onTap: () { + context.go(AppRoutes.dashboard); + }, + child: Row( + // crossAxisAlignment: CrossAxisAlignment.center, + // mainAxisAlignment: MainAxisAlignment.start, + children: [ + Tooltip( + message: 'Back', + child: IconButton( + icon: const Icon(Icons.arrow_left_sharp, size: 25), + onPressed: () async { + context.go(AppRoutes.dashboard); + final prefs = await SharedPreferences.getInstance(); + await prefs.remove('dashboardKeyProvider'); + await prefs.remove('dashboardStatusProvider'); + await prefs.remove('dashboardStaffIdProvider'); + }, + splashRadius: 18, + padding: const EdgeInsets.all(4), + hoverColor: Colors.black12, + constraints: const BoxConstraints(), + ), + ), + const SizedBox(width: 5), // spacing between icon and text + Text( + "Enquiries", + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.w600, + ), + ), + ], + ), + ), + ), + if (_showFilterRow) ...[ + DateFilterRow( + key: ValueKey(SelectedStatus ?? ''), + role: roleId, + id: userId, + selectedStatusVal: SelectedStatus, + onFilterStaff: (val) { + print('Selected Filterd STAFF Id - $val'); + SelectedStaffId = val; + }, + selectedStaffId: SelectedStaffId, + startController: controllers['startDate']!, + endController: controllers['endDate']!, + onStatusChanged: (val) { + SelectedStatus = val; // update parent + }, + formKey: _formKey, + isMobile: ResponsiveLayout.isMobile(context), + onFilter: () { + // call your filter logic + filterDateRange(); + }, + onRefresh: () { + // call your refresh logic + refrshfilterDateRange(); + }, + ), + ], + IconButton( + icon: Icon( + _showFilterRow + ? Icons.close_fullscreen + : Icons.filter_alt_outlined, + ), + tooltip: _showFilterRow ? 'Hide Filters' : 'Show Filters', + onPressed: () { + setState(() { + _showFilterRow = !_showFilterRow; + }); + }, + ), + Spacer(), + ThemedSearchField( + hintText: 'Search', + backgroundColor: Color(0xFFF6F8F8), + onChanged: filterData, + txtHeight: 35, + controller: _searchStaffController, + txtwidth: ResponsiveLayout.isMobile(context) + ? MediaQuery.of(context).size.width * 0.7 + : MediaQuery.of(context).size.width * 0.13, + ), + + ResponsiveLayout.isMobile(context) + ? Spacer() + : SizedBox(width: 10), + + ExportBtn( + sheetName: "Enquiry", + fileName: "Enquiry_list", + data: filteredData, + txt: !ResponsiveLayout.isMobile(context) ? true : false, + displayHeaders: [ + "Received Date & Time", + "Partner", + "Assigned To", + "Insurer", + "Vehicle.No.", + "Insured Name", + "Assigned Date & Time", + "Premium", + "Payment Mode", + "Policy Number", + "Status", + ], + keys: [ + "created_on", + "agent_name", + "assigned_to_name", + "insurer_name", + "reg_no", + "insured_name", + "updated_on", + "premium_amount", + "payment_mode", + "policy_number", + "status", + ], + ), + SizedBox(width: 10), + GestureDetector( + onTap: () async { + // final prefs = await SharedPreferences.getInstance(); + // + // await prefs.remove('enqAgentDataId'); + // ref.read(enquiryIdProvider.notifier).state = null; + // context.go(AppRoutes.tabEnquiry); + // print('Export'); + + // Generate a unique ID for the new row + final newId = DateTime.now().millisecondsSinceEpoch; + + // Clear all existing form controllers + for (String field in tabHeader) { + controllers[field]?.clear(); + } + + // Reset dropdown selections + selectedVehicleType = null; + selectedAgent = null; + selectedInsurer = null; + + // Reset file uploads + docUploadedRCFile = null; + docUploadedIDProof = null; + docUploadedPrevPolicy = null; + selectedRCFile = null; + selectedIdProof = null; + selectedPrevPolicy = null; + + setState(() { + // Create a new empty row data + final newRow = { + 'id': newId.toString(), // Keep as String for consistency + 'created_on': '', + 'agent_name': '', + 'agent_id': '', + 'assigned_to_name': '', + 'insurer_name': '', + 'insured_name': '', + 'reg_no': '', + 'vehicle_type': '', + 'vehicle_type_id': '', + 'email': '', + 'mobile': '', + 'remarks': '', + 'updated_on': '', + 'premium_amount': '', + 'payment_mode': '', + 'policy_number': '', + 'status': '', + }; + + // Insert at the beginning of ALL lists + getStaffData.insert(0, newRow); + originalData.insert(0, newRow); + filteredData.insert(0, newRow); + + // Set to editing mode immediately + isEditingRow[newId] = true; + + // Store the ID as string + idPrimary = newId.toString(); + selectedId = newId.toString(); + + // Reset to first page to see the new row + currentPage = 1; + }); + }, + child: Container( + padding: EdgeInsets.all(8.0), + decoration: BoxDecoration( + color: Color(0xFF425B5B), + borderRadius: BorderRadius.circular(8.0), + ), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Tooltip( + message: 'Raise Enquiry', + child: Icon(Icons.add, color: Colors.white, size: 15), + ), + + // if (!ResponsiveLayout.isMobile(context)) ...[ + // SizedBox(width: 10), + // Text( + // 'Raise Enquiry', + // style: GoogleFonts.inter( + // color: Colors.white, + // fontWeight: FontWeight.w600, + // fontSize: 14, + // ), + // ), + // ], + ], + ), + ), + ), + ], + ), + ), + // SizedBox(height: 10), + + // Container( + // decoration: BoxDecoration( + // color: const Color(0xFFEDF6F5), + // borderRadius: BorderRadius.circular(6), + // ), + // padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 16), + // child: Row( + // children: const [ + // Expanded(flex: 1, child: Text('S.No', style: _headerStyle)), + // Expanded( + // flex: 2, + // child: Text('Received Date', style: _headerStyle), + // ), + // Expanded(flex: 2, child: Text('Partner *', style: _headerStyle)), + // Expanded( + // flex: 2, + // child: Text('Assigned To *', style: _headerStyle), + // ), + // Expanded(flex: 2, child: Text('Broker *', style: _headerStyle)), + // Expanded(flex: 3, child: Text('Insurer *', style: _headerStyle)), + // Expanded( + // flex: 3, + // child: Text('Insured Name *', style: _headerStyle), + // ), + // Expanded( + // flex: 2, + // child: Text('Vehicle No *', style: _headerStyle), + // ), + // Expanded( + // flex: 2, + // child: Text('Vehicle Type *', style: _headerStyle), + // ), + // Expanded(flex: 2, child: Text('Email', style: _headerStyle)), + // Expanded(flex: 2, child: Text('Mobile', style: _headerStyle)), + // Expanded(flex: 2, child: Text('Documents', style: _headerStyle)), + // Expanded(flex: 2, child: Text('Remarks', style: _headerStyle)), + // Expanded( + // flex: 2, + // child: Text('Assigned Date', style: _headerStyle), + // ), + // Expanded(flex: 2, child: Text('Premium', style: _headerStyle)), + // Expanded( + // flex: 2, + // child: Text('Payment Mode', style: _headerStyle), + // ), + // Expanded( + // flex: 2, + // child: Text('Policy Number', style: _headerStyle), + // ), + // Expanded(flex: 2, child: Text('Status', style: _headerStyle)), + // Expanded(flex: 1, child: Text('Action', style: _headerStyle)), + // ], + // ), + // ), + ResponsiveLayout.isMobile(context) + ? _buildDataTable(context) + : Expanded(child: _buildDataTable(context)), + ], + ); + } + + List _buildPopupDownload(BuildContext context, selectId, editing) { + bool isMobile = ResponsiveLayout.isMobile(context); + return [ + editing + ? Container( + // color: Colors.red.shade50, + padding: EdgeInsets.only(top: 10), + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + + children: [ + buildUploadRCDocument(context), + SizedBox(width: 3), + buildUploadIDDocument(context), + SizedBox(width: 3), + buildUploadPolicyDocument(context), + ], + ), + ) + : Row( + children: [ + buildDocRC(context, selectId), + SizedBox(width: 3), + buildDocIdProof(context, selectId), + SizedBox(width: 3), + buildDocPrevPolicy(context, selectId), + ], + ), + ]; + } + + Widget _buildDataTable(BuildContext context) { + if (filteredData.isEmpty) { + return const SizedBox( + height: 50, + child: Center(child: Text('No available data')), + ); + } + + final sortedData = [..._paginatedData]; + final startIndex = (currentPage - 1) * itemsPerPage; + final isDesktop = !ResponsiveLayout.isMobile(context); + + // Build both-direction scrollable DataTable + return LayoutBuilder( + builder: (context, constraints) { + double minWidth = isDesktop ? constraints.maxWidth : 1300; + + return SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: SingleChildScrollView( + scrollDirection: Axis.vertical, + child: ConstrainedBox( + constraints: BoxConstraints(minWidth: minWidth), + child: DataTable( + headingRowColor: WidgetStatePropertyAll(Color(0xFFEDF6F5)), + dividerThickness: 0.5, + headingRowHeight: 30, + columnSpacing: isDesktop ? 20.0 : 16.0, + border: TableBorder( + horizontalInside: BorderSide( + width: 0.5, + color: Colors.grey.shade200, + ), + ), + columns: [ + // DataColumn(label: Text('S.No', style: _headerStyle)), + const DataColumn( + label: Text('Received Date', style: _headerStyle), + ), + const DataColumn( + label: Text('Partner *', style: _headerStyle), + ), + const DataColumn( + label: Text('Assigned To *', style: _headerStyle), + ), + const DataColumn( + label: Text('Broker *', style: _headerStyle), + ), + const DataColumn( + label: Text('Insurer *', style: _headerStyle), + ), + const DataColumn( + label: Text('Insured Name *', style: _headerStyle), + ), + const DataColumn( + label: Text('Vehicle No *', style: _headerStyle), + ), + const DataColumn( + label: Text('Vehicle Type *', style: _headerStyle), + ), + const DataColumn(label: Text('Email', style: _headerStyle)), + const DataColumn(label: Text('Mobile', style: _headerStyle)), + const DataColumn( + label: Text('Documents', style: _headerStyle), + ), + const DataColumn(label: Text('Remarks', style: _headerStyle)), + if (roleId == 'manager') ...const [ + DataColumn(label: Text('Action', style: _headerStyle)), + ], + const DataColumn( + label: Text('Assigned Date', style: _headerStyle), + ), + const DataColumn(label: Text('Premium', style: _headerStyle)), + const DataColumn( + label: Text('Payment Mode', style: _headerStyle), + ), + const DataColumn( + label: Text('Policy Number', style: _headerStyle), + ), + const DataColumn(label: Text('Status', style: _headerStyle)), + ], + rows: sortedData.asMap().entries.map((entry) { + final index = entry.key; + final item = entry.value; + final sno = startIndex + index + 1; + return _buildDataRow(item, sno); + }).toList(), + ), + ), + ), + ); + }, + ); + } + + DataRow _buildDataRow(Map item, int sno) { + final id = int.tryParse(item['id'].toString()) ?? 0; + final selectId = item['id'].toString() ?? '0'; + + print('idPrimary - $idPrimary'); + final editing = isEditingRow[id] ?? false; + final controllers = rowControllers[id]; + + return DataRow( + cells: [ + // S.No. + // DataCell(Text('$sno', style: _dataBold)), + + // Created On + DataCell( + Row( + children: [ + // Text(item['created_on'], style: _dataBold), + Text( + formatDateTimeForTable(item['created_on'], newLine: true), + style: _dataBold, + ), + ], + ), + ), + + // Agent Name + DataCell( + editing + ? buildAgentName(context) + : Text(item['agent_name'] ?? '-', style: _dataBold), + ), + + // Assigned To + DataCell( + editing + ? buildSelectStaffMem(context) + : Text(item['assigned_to_name'] ?? '-', style: _dataBold), + ), + + DataCell( + editing + ? buildBroker(context) + : Text(item['broker_name'] ?? '-', style: _dataBold), + ), + + // Insurer Name + DataCell( + editing + ? buildInsurer(context) + : Text(item['insurer_short_name'] ?? '-', style: _dataBold), + ), + + DataCell( + editing + ? buildInsuredName(context) + : Text(item['insured_name'] ?? '-', style: _dataBold), + ), + // Vehicle No + DataCell( + editing + ? buildVehicleNumber(context) + : Text(item['reg_no'] ?? '-', style: _dataBold), + ), + DataCell( + editing + ? buildVehicleType(context) + : Text( + _wrapText(item['vehicle_type'] ?? '-', 13), + style: _dataBold, + softWrap: true, + ), + ), + + DataCell( + editing + ? buildEmail(context) + : Text(item['email'] ?? '-', style: _dataBold), + ), + DataCell( + editing + ? buildPhNumber(context) + : Text(item['mobile'] ?? '-', style: _dataBold), + ), + + DataCell( + Builder( + builder: (buttonContext) => Material( + color: Colors.white, + child: InkWell( + hoverColor: const Color(0xFFEAF6F4), + onTap: () async { + // ✅ Get overlay and button position RELATIVE to InkWell + final RenderBox button = + buttonContext.findRenderObject() as RenderBox; + final RenderBox overlay = + Overlay.of(buttonContext).context.findRenderObject() + as RenderBox; + + final Offset position = button.localToGlobal( + Offset.zero, + ancestor: overlay, + ); + + await showMenu( + context: buttonContext, + position: RelativeRect.fromLTRB( + position.dx, + position.dy + button.size.height, + overlay.size.width, + 0, + ), + items: [ + PopupMenuItem( + child: Column( + mainAxisSize: MainAxisSize.min, + children: _buildPopupDownload( + buttonContext, + selectId, + editing, + ), + ), + ), + ], + color: Colors.white, + ); + }, + child: Tooltip( + message: editing + ? 'Click To Upload Documents' + : 'Click To View Documents', + waitDuration: const Duration(milliseconds: 500), + showDuration: const Duration(seconds: 2), + child: Icon( + editing ? Icons.upload_file : Icons.download_for_offline, + color: editing ? Colors.blue : Colors.green, + size: 22, + ), + ), + ), + ), + ), + ), + + DataCell( + editing + ? buildRemarks(context) + : Tooltip( + message: item['remarks'] ?? '-', // full content on hover + waitDuration: const Duration(milliseconds: 400), + showDuration: const Duration(seconds: 2), + child: Text( + _truncateText(item['remarks'] ?? '-', 10), + style: _dataBold, + overflow: TextOverflow.ellipsis, + ), + ), + ), + + // Actions + if (roleId == 'manager') ...[ + DataCell( + // (editing && (item['status'] == 'Awaiting Proposal')) + editing + ? Row( + children: [ + IconButton( + icon: const Icon(Icons.check, color: Colors.green), + tooltip: "Save", + onPressed: () => handleSave(id), + ), + IconButton( + icon: const Icon(Icons.close, color: Colors.red), + tooltip: "Cancel", + onPressed: () => handleCancel(id), + ), + ], + ) + : Row( + children: [ + IconButton( + icon: const Icon(Icons.edit, color: Colors.blue), + tooltip: "Edit", + onPressed: () { + handleEdit(item); + }, + ), + ], + ), + ), + ], + // Assigned Date (formatted) + DataCell( + Text( + // item['updated_on'] ?? '-', + formatDateTimeForTable(item['updated_on'], newLine: true), + style: _dataBold, + softWrap: true, + maxLines: 3, + ), + ), + + // Premium + DataCell( + // ((item['premium_amount'] != null) && + // (item['premium_amount'] != '') && + // ((item['status'] != 'Awaiting Proposal') || + // (item['status'] == 'Proposal Created') || + // (item['status'] == 'Proposal Rejected'))) + (item['status'] == 'Proposal Created' || + item['status'] == 'Proposal Rejected') + ? InkWell( + onTap: () { + // handle approval logic + + handleProposalAccept(context, item['id']); + }, + child: Text( + 'Click To\nApprove', + style: GoogleFonts.inter( + color: Colors.green, + fontSize: 11, + fontWeight: FontWeight.w700, + ), + ), + ) + : Text( + item['premium_amount']?.toString() ?? '-', + style: _dataBold, + ), + ), + + // Payment Mode + DataCell(Text(item['payment_mode'] ?? '-', style: _dataBold)), + + // Policy Number + DataCell( + Text( + item['policy_number'] ?? '-', + style: _dataBold, + softWrap: true, + maxLines: 3, + ), + ), + + // Status + // DataCell(Text(item['status'] ?? '-', style: _dataBold)), + DataCell( + Material( + color: Colors.white, + child: InkWell( + hoverColor: Color(0xFFEAF6F4), + onTap: () async { + var status = item['status']; + var enqId = item['id']; + + final prefs = await SharedPreferences.getInstance(); + + // ✅ Remove old value (if any) + await prefs.remove('enqStaffDataId'); + + // ✅ Save the new id + await prefs.setString('enqStaffDataId', enqId.toString()); + ref.read(quotationStaffIdProvider.notifier).state = enqId; + buildStatusActions(context, status, enqId); + }, + child: Tooltip( + message: 'Click to View or Process Enquiry', // + waitDuration: const Duration(milliseconds: 500), // optional + showDuration: const Duration(seconds: 2), // optional + child: Text( + item['status'] ?? '-', + style: _dataBold, + overflow: TextOverflow.ellipsis, // + ), + ), + // child: Text(item['status'] ?? '-', style: _dataBold), + ), + ), + ), + ], + ); + } + + static final _dataBold = TextStyle( + fontSize: 12, + + fontWeight: FontWeight.w500, + color: Colors.black, + // color: Color(0xFF000000), + ); + final _tableDataTimeStyle = GoogleFonts.inter( + fontSize: 10, + fontWeight: FontWeight.w400, + ); + static final _dataBoldsmall = TextStyle( + fontSize: 12, + + fontWeight: FontWeight.w400, + color: Color(0xFF000000), + ); + + static final _dataSub = TextStyle( + fontSize: 10, + fontWeight: FontWeight.w300, + color: Color(0xFF585757), + ); + + static const _headerStyle = TextStyle( + color: Colors.black, + fontWeight: FontWeight.w500, + ); + static const _cardheaderStyle = TextStyle( + color: Colors.black, + fontWeight: FontWeight.w600, + fontSize: 12, + ); + + static final TextStyle _textStyle = TextStyle( + fontSize: 12, + fontWeight: FontWeight.w600, + ); + + static const _cardBodyStyle = TextStyle( + color: Color(0xFF545454), + fontWeight: FontWeight.w400, + fontSize: 12, + ); + + String _truncateText(String text, int limit) { + if (text.length <= limit) return text; + return '${text.substring(0, limit)}...'; + } + + String _wrapText(String text, int chunkSize) { + if (text.isEmpty) return '-'; + final buffer = StringBuffer(); + for (int i = 0; i < text.length; i += chunkSize) { + int end = (i + chunkSize < text.length) ? i + chunkSize : text.length; + buffer.writeln(text.substring(i, end)); // adds '\n' after each chunk + } + return buffer.toString().trim(); + } + + String formatDateTimeForTable(String? input, {bool newLine = false}) { + if (input == null || input.trim().isEmpty) return '-'; + + final s = input.trim(); + DateTime dt; + + try { + // Case 1: ISO-like (yyyy-MM-dd ...), DateTime.parse will handle + if (RegExp(r'^\d{4}-\d{2}-\d{2}').hasMatch(s)) { + dt = DateTime.parse(s); + } + // Case 2: day-month-year at start (e.g. 13-11-2025 07:28 AM or 13-11-2025 07:28) + else if (RegExp(r'^\d{2}-\d{2}-\d{4}').hasMatch(s)) { + // Try with am/pm (hh:mm a) + if (RegExp(r'\b(am|pm)\b', caseSensitive: false).hasMatch(s)) { + dt = DateFormat('dd-MM-yyyy hh:mm a').parse(s); + } + // Try 24-hour time "dd-MM-yyyy HH:mm" or just date "dd-MM-yyyy" + else if (RegExp(r'^\d{2}-\d{2}-\d{4}\s+\d{1,2}:\d{2}').hasMatch(s)) { + dt = DateFormat('dd-MM-yyyy HH:mm').parse(s); + } else { + dt = DateFormat('dd-MM-yyyy').parse(s); + } + } + // Case 3: fallback — try parse with DateTime.parse (may throw) + else { + dt = DateTime.parse(s); + } + + final dateStr = DateFormat('dd-MM-yyyy').format(dt); + final timeStr = DateFormat('hh:mm a').format(dt); // e.g. 07:28 AM + + return newLine ? '$dateStr\n$timeStr' : '$dateStr | $timeStr'; + } catch (e) { + // If parsing fails, return original string as fallback (or '-' if empty) + return s.isNotEmpty ? s : '-'; + } + } +} diff --git a/lib/presentation/screens/staff/Enquiry/enquiry_list.dart b/lib/presentation/screens/staff/Enquiry/enquiry_list.dart index 2ba9b8a..4e9ae22 100644 --- a/lib/presentation/screens/staff/Enquiry/enquiry_list.dart +++ b/lib/presentation/screens/staff/Enquiry/enquiry_list.dart @@ -1084,10 +1084,6 @@ class EnquiryStaffState extends ConsumerState { color: Colors.white, child: InkWell( hoverColor: Color(0xFFEAF6F4), - // hoverColor: const Color(0xffD9EBE8), - // focusColor: const Color(0xffD9EBE8), - // splashColor: const Color(0xffD9EBE8), - // highlightColor: const Color(0xffD9EBE8), onTap: () async { // ✅ Get overlay and button position RELATIVE to InkWell final RenderBox button = diff --git a/lib/presentation/screens/staff/Enquiry/tabs/policyTab.dart b/lib/presentation/screens/staff/Enquiry/tabs/policyTab.dart index cbc7a33..8215d33 100644 --- a/lib/presentation/screens/staff/Enquiry/tabs/policyTab.dart +++ b/lib/presentation/screens/staff/Enquiry/tabs/policyTab.dart @@ -73,6 +73,26 @@ class PolicyStaffTabState extends ConsumerState { 'issueDate', 'startDate', 'endDate', + + 'tp', + 'od', + 'pa', + 'cgst', + 'sgst', + 'igst', + 'rto_state_code', + 'rto_city_code', + 'weight', + 'fuel_type', + 'date_of_registration', + 'year_of_manufacture', + 'engine_no', + 'chassis_no', + 'make', + 'model', + 'cubic_capacity', + 'vehicle_type', + 'premiumTOT', ]; late String isActive = "1"; PlatformFile? docUploadedFile; @@ -100,6 +120,7 @@ class PolicyStaffTabState extends ConsumerState { dynamic choosedPolcyId; dynamic roleId; + dynamic enqBrokerName; List> getVehicleTypeData = []; List> filteredVechicleData = []; @@ -118,10 +139,19 @@ class PolicyStaffTabState extends ConsumerState { "issued_date": controllers["issueDate"]?.text, "start_date": controllers["startDate"]?.text, "end_date": controllers["endDate"]?.text, - "premium_amount": controllers["premium_amount"]?.text, + // "premium_amount": controllers["premium_amount"]?.text, // "premium_amount": controllers["policyPremiumAmount"]?.text, "policy_number": controllers["policyNumber"]?.text, "payment_mode": controllers["policyPaymentMode"]?.text, + + "tp": controllers["tp"]?.text, + "od": controllers["od"]?.text, + "pa": controllers["pa"]?.text, + "cgst": controllers["cgst"]?.text, + "sgst": controllers["sgst"]?.text, + "igst": controllers["igst"]?.text, + "premium_amount": controllers["premiumTOT"]?.text, + "manager_id": managerId, // "policy_pdf_file_name": "policy_doc.pdf", // "policy_payment_receipt_file_name": "receipt_doc.pdf", @@ -148,6 +178,15 @@ class PolicyStaffTabState extends ConsumerState { for (String field in tabHeader) { controllers[field] = TextEditingController(); } + + // Add listeners to text fields + controllers["tp"]?.addListener(calculateAmount); + controllers["od"]?.addListener(calculateAmount); + controllers["pa"]?.addListener(calculateAmount); + controllers["cgst"]?.addListener(calculateAmount); + controllers["sgst"]?.addListener(calculateAmount); + controllers["igst"]?.addListener(calculateAmount); + // updateData(); _initializeToken(); // Future.microtask(() { @@ -165,10 +204,17 @@ class PolicyStaffTabState extends ConsumerState { managerId = ref.read(managerIdProvider); // ✅ use read userId = ref.read(userIdProvider); // ✅ use read roleId = ref.read(userRoleProvider); // ✅ use read + enqBrokerName = ref.read(enqBrokerNameProvider); + print('QmanagerId - $managerId'); print('QuserId - $userId'); enqQuotation = ref.read(quotationStaffIdProvider); // ✅ use read print('QenqQuotation - $enqQuotation'); + + controllers["cgst"]?.text = '0'; + controllers["sgst"]?.text = '0'; + controllers["igst"]?.text = '0'; + if (enqQuotation != null) { _loadData(enqQuotation); } @@ -178,9 +224,15 @@ class PolicyStaffTabState extends ConsumerState { Future _restoreManagerId(WidgetRef ref) async { final prefs = await SharedPreferences.getInstance(); - final String? savedId = prefs.getString( - 'enqStaffDataId', + final String? savedId = prefs.getString('enqStaffDataId'); + final String? savedBrokerName = prefs.getString( + 'enqBrokerName', ); // already a string + + if (savedBrokerName != null) { + ref.read(enqBrokerNameProvider.notifier).state = savedBrokerName; + } + if (savedId != null) { ref.read(quotationStaffIdProvider.notifier).state = savedId; print("savedenqStaffDataId ID restored: $savedId"); @@ -189,6 +241,23 @@ class PolicyStaffTabState extends ConsumerState { } } + void calculateAmount() { + print('calculateAmount 1'); + setState(() { + double tp = double.tryParse(controllers["tp"]?.text ?? '0') ?? 0; + double od = double.tryParse(controllers["od"]?.text ?? '0') ?? 0; + double pa = double.tryParse(controllers["pa"]?.text ?? '0') ?? 0; + double cgst = double.tryParse(controllers["cgst"]?.text ?? '0') ?? 0; + double sgst = double.tryParse(controllers["sgst"]?.text ?? '0') ?? 0; + double igst = double.tryParse(controllers["igst"]?.text ?? '0') ?? 0; + + double total = tp + od + pa + cgst + sgst + igst; + + print('Val : $total'); + controllers["premiumTOT"]!.text = total.toStringAsFixed(2); + }); + } + @override void dispose() { // Dispose all TextEditingControllers @@ -315,6 +384,16 @@ class PolicyStaffTabState extends ConsumerState { policyData['start_date']?.toString() ?? ''; controllers["endDate"]?.text = policyData['end_date']?.toString() ?? ''; + controllers["tp"]?.text = policyData['tp']?.toString() ?? ''; + controllers["od"]?.text = policyData['od']?.toString() ?? ''; + controllers["pa"]?.text = policyData['pa']?.toString() ?? ''; + + controllers["cgst"]?.text = policyData['cgst']?.toString() ?? ''; + controllers["sgst"]?.text = policyData['sgst']?.toString() ?? ''; + controllers["igst"]?.text = policyData['igst']?.toString() ?? ''; + controllers["premiumTOT"]?.text = + policyData['premium_amount']?.toString() ?? ''; + // RC FILE String? rcPath = policyData["policy_pdf_file_name"]; if (rcPath != null && rcPath.isNotEmpty) { @@ -372,6 +451,18 @@ class PolicyStaffTabState extends ConsumerState { controllers["endDate"]?.text = _formatDate(policyData['end_date']!.toString()) ?? ''; + final premium = policyData['premium']; + if (premium != null) { + final taxes = premium['taxes'] ?? {}; + controllers["tp"]?.text = premium["tp"]?.toString() ?? '0'; + controllers["od"]?.text = premium["od"]?.toString() ?? '0'; + controllers["pa"]?.text = premium["pa"]?.toString() ?? '0'; + controllers["cgst"]?.text = taxes["cgst"]?.toString() ?? '0'; + controllers["sgst"]?.text = taxes["sgst"]?.toString() ?? '0'; + controllers["igst"]?.text = taxes["igst"]?.toString() ?? '0'; + controllers["premiumTOT"]?.text = premium["total"]?.toString() ?? '0'; + } + // RC FILE String? rcPath = policyData["policy_pdf_file_name"]; if (rcPath != null && rcPath.isNotEmpty) { @@ -392,17 +483,26 @@ class PolicyStaffTabState extends ConsumerState { if (!_formKey.currentState!.validate()) return; setState(() { if (_formKey.currentState!.validate()) { + calculateAmount(); dataDetails(); final dataSet = dataDetails(); print("dataSetPolicy - $dataSet"); + final bool isUpdating = choosedPolcyId != null; + + if (!isUpdating && enqBrokerName == 'Nhance') { + ToastHelper.showErrorToast(context, "Policy PDF is required"); + return; + } + // (enqBrokerName != 'Nhance') // File validations - // if ((docPDFUploadedFile == null || docPDFUploadedFile?.bytes == null) && - // (docUploadedPDFFileUrlFromApi == null || - // docUploadedPDFFileUrlFromApi!.isEmpty)) { - // ToastHelper.showErrorToast(context, "Policy PDF is required"); - // return; - // } + if ((enqBrokerName != 'Nhance') && + (docPDFUploadedFile == null || docPDFUploadedFile?.bytes == null) && + (docUploadedPDFFileUrlFromApi == null || + docUploadedPDFFileUrlFromApi!.isEmpty)) { + ToastHelper.showErrorToast(context, "Policy PDF is required"); + return; + } createUserData(dataSet); } else { // isDi sable = false; @@ -482,11 +582,9 @@ class PolicyStaffTabState extends ConsumerState { // final bool isUpdating = false; final uri = Uri.parse( - '${Env.apiUrl}policy/updatePolicy', - // isUpdating - // ? - // : - // '${Env.apiUrl}policy/createPolicy', + isUpdating + ? '${Env.apiUrl}policy/updatePolicy' + : '${Env.apiUrl}policy/createPolicy', ); if (_token == null) { throw Exception('Token not found. Please log in.'); @@ -657,6 +755,10 @@ class PolicyStaffTabState extends ConsumerState { // optional if available 'policy_pdf_file_name': jsonResponse['data']?['policy_pdf_file_name'], + + // ✅ include premium details here + 'premium': + jsonResponse['data']?['file_read_data']?['value']?['premium'], // optional if your API returns it }; @@ -699,56 +801,57 @@ class PolicyStaffTabState extends ConsumerState { return Container( child: Column( children: [ - Expanded( - child: Container( - // color: Colors.green, - decoration: BoxDecoration( - color: Color(0xffEDF6F5), - borderRadius: BorderRadius.circular(10.0), - ), - width: MediaQuery.of(context).size.width, - // height: MediaQuery.of(context).size.height * 0.8, - padding: EdgeInsets.all(26.0), + // Expanded( + // child: + Container( + // color: Colors.green, + decoration: BoxDecoration( + // color: Colors.orange.shade50, + color: Color(0xffEDF6F5), + borderRadius: BorderRadius.circular(10.0), + ), + width: MediaQuery.of(context).size.width, + height: MediaQuery.of(context).size.height * 0.58, + padding: EdgeInsets.all(16.0), + child: SingleChildScrollView( + scrollDirection: Axis.vertical, child: Column( mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - buildFormFields(context), - if (roleId != 'manager') ...[ - Row( - mainAxisAlignment: MainAxisAlignment.end, - children: [ - InkWell( - onTap: () { - handleSave(); - }, - child: Container( - padding: EdgeInsets.symmetric( - horizontal: 45.0, - vertical: 8, - ), - - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(8.0), - color: Color(0xFF425B5B), - ), - child: Text( - // !hasPolicyData ? 'Submit' : 'Update', - 'Submit', - style: TextStyle( - color: Colors.white, - fontSize: 12, - fontWeight: FontWeight.w600, - ), - ), - ), - ), - ], - ), - ], - ], + children: [buildFormFields(context)], ), ), ), + SizedBox(height: 1), + // if (roleId != 'manager') ...[ + Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + InkWell( + onTap: () { + handleSave(); + }, + child: Container( + padding: EdgeInsets.symmetric(horizontal: 45.0, vertical: 8), + + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(8.0), + color: Color(0xFF425B5B), + ), + child: Text( + // !hasPolicyData ? 'Submit' : 'Update', + 'Submit', + style: TextStyle( + color: Colors.white, + fontSize: 12, + fontWeight: FontWeight.w600, + ), + ), + ), + ), + ], + ), + // ], + // ), ], ), // ), @@ -911,27 +1014,136 @@ class PolicyStaffTabState extends ConsumerState { } Widget buildFormFields(BuildContext context) { + return Form( + key: _formKey, + child: Column( + children: [ + Row( + // mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + buildUploadPolicyPdf(context), + SizedBox(width: 20), + Paymentmode(context), + ], + ), + SizedBox(height: 10), + Row( + // mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + policynumber(context), + SizedBox(width: 20), + policyissuedate(context), + SizedBox(width: 20), + policystartdate(context), + SizedBox(width: 20), + policyenddate(context), + ], + ), + + Row( + crossAxisAlignment: CrossAxisAlignment.start, + + // mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + buildThirdParty(context), + buildOwnDamage(context), + buildPersonalAccident(context), + ], + ), + + SizedBox(width: 20), + Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + buildCGST(context), + buildSGST(context), + buildIGST(context), + buildPremiumTotal(context), + ], + ), + ], + ), + ], + ), + ); + } + + Widget buildFormFields1(BuildContext context) { return Form( key: _formKey, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ + // _buildResponsiveRow( + // context, + // buildUploadPolicyPdf(context), + // Paymentmode(context), + policynumber(context), + // SizedBox.shrink(), + // + // // policyissuedate(context), + // // policystartdate(context), + // // policyenddate(context), + // ), + // const SizedBox(height: 15), + // Text("Policy TimePeriod", style: _textStrongStyle), + if (!ResponsiveLayout.isMobile(context)) const SizedBox(height: 10), _buildResponsiveRow( context, - buildUploadPolicyPdf(context), - Paymentmode(context), - policynumber(context), - ), - const SizedBox(height: 15), - Text("Policy TimePeriod", style: _textStrongStyle), - if (!ResponsiveLayout.isMobile(context)) const SizedBox(height: 15), - _buildResponsiveRow( - context, - policyissuedate(context), policystartdate(context), policyenddate(context), + SizedBox.shrink(), ), + // const SizedBox(height: 15), + // Text("Policy TimePeriod", style: _textStrongStyle), + if (!ResponsiveLayout.isMobile(context)) const SizedBox(height: 10), + _buildResponsiveRow( + context, + + buildThirdParty(context), + buildOwnDamage(context), + buildPersonalAccident(context), + SizedBox.shrink(), + ), + if (!ResponsiveLayout.isMobile(context)) const SizedBox(height: 10), + _buildResponsiveRow( + context, + buildCGST(context), + buildSGST(context), + buildIGST(context), + SizedBox.shrink(), + + // buildRTOStateCode(context), + // buildRTOCityCode(context), + ), + // if (!ResponsiveLayout.isMobile(context)) const SizedBox(height: 10), + // _buildResponsiveRow( + // context, + // buildWeight(context), + // buildFuelType(context), + // buildDateOfRegistration(context), + // buildYearOfManufacture(context), + // buildEngineNo(context), + // ), + // + // if (!ResponsiveLayout.isMobile(context)) const SizedBox(height: 10), + // _buildResponsiveRow( + // context, + // buildChassisNo(context), + // buildMake(context), + // buildModel(context), + // buildCubicCapacity(context), + // buildVehicleType(context), + // // SizedBox.shrink(), + // ), // if (!ResponsiveLayout.isMobile(context)) const SizedBox(height: 10), // @@ -965,6 +1177,9 @@ class PolicyStaffTabState extends ConsumerState { Widget first, Widget second, Widget third, + Widget fourth, + // Widget fifth, + // Widget Six, ) { if (ResponsiveLayout.isMobile(context)) { // Stack vertically @@ -974,41 +1189,21 @@ class PolicyStaffTabState extends ConsumerState { return Row( children: [ Expanded(child: first), - const SizedBox(width: 25), + const SizedBox(width: 20), Expanded(child: second), - const SizedBox(width: 25), + const SizedBox(width: 20), Expanded(child: third), + const SizedBox(width: 20), + Expanded(child: fourth), + // const SizedBox(width: 20), + // Expanded(child: fifth), + // const SizedBox(width: 20), + // Expanded(child: Six), ], ); } } - // Widget buildName(BuildContext context) { - // return buildResponsiveField( - // label: "Registration Number", - // field: ThemedFormField( - // controller: controllers['name']!, - // validator: (value) => Validators.requiredField(value, "name"), - // txtwidth: ResponsiveLayout.isMobile(context) - // ? null - // : MediaQuery.of(context).size.width * 0.26, - // ), - // ); - // } - // - // Widget buildInsusrer(BuildContext context) { - // return buildResponsiveField( - // label: "Insusrer", - // field: ThemedFormField( - // controller: controllers['email']!, - // validator: (value) => Validators.email(value, "email"), - // txtwidth: ResponsiveLayout.isMobile(context) - // ? null - // : MediaQuery.of(context).size.width * 0.26, - // ), - // ); - // } - Widget registernumber(BuildContext context) { return Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -1061,24 +1256,24 @@ class PolicyStaffTabState extends ConsumerState { ); } - Widget buildPremiumAmnt(BuildContext context) { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text('Premium Amount *', style: _textStyle), - SizedBox(height: 10), - ThemedFormField( - controller: controllers['premium_amount']!, - readOnly: true, - validator: (value) => - Validators.doubleNumber(value, "Premium Amount"), - txtwidth: ResponsiveLayout.isMobile(context) - ? null - : MediaQuery.of(context).size.width * 0.26, - ), - ], - ); - } + // Widget buildPremiumAmnt(BuildContext context) { + // return Column( + // crossAxisAlignment: CrossAxisAlignment.start, + // children: [ + // Text('Premium Amount *', style: _textStyle), + // SizedBox(height: 10), + // ThemedFormField( + // controller: controllers['premium_amount']!, + // readOnly: true, + // validator: (value) => + // Validators.doubleNumber(value, "Premium Amount"), + // txtwidth: ResponsiveLayout.isMobile(context) + // ? null + // : MediaQuery.of(context).size.width * 0.26, + // ), + // ], + // ); + // } Widget buildPlanType(BuildContext context) { return Column( @@ -1114,24 +1309,24 @@ class PolicyStaffTabState extends ConsumerState { ], ); } - - Widget premiumamount(BuildContext context) { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text('Premium Amount ', style: _textStyle), - SizedBox(height: 10), - ThemedFormField( - controller: controllers['premium_amount']!, - // validator: (value) => - // Validators.requiredField(value, "policyPremiumAmount"), - txtwidth: ResponsiveLayout.isMobile(context) - ? null - : MediaQuery.of(context).size.width * 0.26, - ), - ], - ); - } + // + // Widget premiumamount(BuildContext context) { + // return Column( + // crossAxisAlignment: CrossAxisAlignment.start, + // children: [ + // Text('Premium Amount ', style: _textStyle), + // SizedBox(height: 10), + // ThemedFormField( + // controller: controllers['premium_amount']!, + // // validator: (value) => + // // Validators.requiredField(value, "policyPremiumAmount"), + // txtwidth: ResponsiveLayout.isMobile(context) + // ? null + // : MediaQuery.of(context).size.width * 0.26, + // ), + // ], + // ); + // } Widget policynumber(BuildContext context) { return Column( @@ -1147,25 +1342,30 @@ class PolicyStaffTabState extends ConsumerState { ], txtwidth: ResponsiveLayout.isMobile(context) ? null - : MediaQuery.of(context).size.width * 0.26, + : MediaQuery.of(context).size.width * 0.15, ), ], ); } Widget Paymentmode(BuildContext context) { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, + return Row( + crossAxisAlignment: CrossAxisAlignment.center, + // mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text('Payment Mode *', style: _textStyle), - SizedBox(height: 10), + SizedBox(width: 25), ThemedFormField( controller: controllers['policyPaymentMode']!, + backgroundColor: Color(0xffEDF6F5), + errorBorderColor: Color(0xffEDF6F5), + hintText: 'Online / Cash', + // borderColor: Color(0xffEDF6F5), validator: (value) => Validators.requiredField(value, "policyPaymentMode"), txtwidth: ResponsiveLayout.isMobile(context) ? null - : MediaQuery.of(context).size.width * 0.26, + : MediaQuery.of(context).size.width * 0.15, ), ], ); @@ -1181,7 +1381,7 @@ class PolicyStaffTabState extends ConsumerState { hintText: "Select Date", txtwidth: ResponsiveLayout.isMobile(context) ? null - : MediaQuery.of(context).size.width * 0.26, + : MediaQuery.of(context).size.width * 0.14, txtheight: 50, // backgroundColor: const Color(0xFFECECEC), validator: (value) => Validators.requiredField(value, "date"), @@ -1209,7 +1409,7 @@ class PolicyStaffTabState extends ConsumerState { hintText: "Select Date", txtwidth: ResponsiveLayout.isMobile(context) ? null - : MediaQuery.of(context).size.width * 0.26, + : MediaQuery.of(context).size.width * 0.14, txtheight: 50, // backgroundColor: const Color(0xFFECECEC), validator: (value) => Validators.requiredField(value, "date"), @@ -1250,7 +1450,7 @@ class PolicyStaffTabState extends ConsumerState { hintText: "Select Date", txtwidth: ResponsiveLayout.isMobile(context) ? null - : MediaQuery.of(context).size.width * 0.26, + : MediaQuery.of(context).size.width * 0.14, txtheight: 50, // backgroundColor: const Color(0xFFECECEC), // validator: (value) => Validators.requiredField(value, "date"), @@ -1330,12 +1530,16 @@ class PolicyStaffTabState extends ConsumerState { } Widget buildUploadPolicyPdf(BuildContext context) { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.start, + return Row( + crossAxisAlignment: CrossAxisAlignment.center, + // mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Text('Upload Policy PDF * ', style: _textStyle), - const SizedBox(height: 10), + SizedBox( + width: MediaQuery.of(context).size.width * 0.15, + child: Text('Upload Policy PDF * ', style: _textStyle), + ), + // const SizedBox(height: 10), + // SizedBox(width: 20), Row( mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.center, @@ -1343,36 +1547,44 @@ class PolicyStaffTabState extends ConsumerState { ThemedUploadField( hintText: selectedPDFFileNames ?? "Upload Document", allowedExtensions: ['pdf'], + backgroundColor: Color(0xffEDF6F5), txtwidth: ResponsiveLayout.isMobile(context) ? null : (docUploadedPDFFileUrlFromApi != null) - ? MediaQuery.of(context).size.width * 0.15 - : MediaQuery.of(context).size.width * 0.175, + ? MediaQuery.of(context).size.width * 0.135 + // ? MediaQuery.of(context).size.width * 0.12 + : MediaQuery.of(context).size.width * 0.15, // backgroundColor: Color(0xFFEDF6F5), onFileSelected: (fileName, file) async { print("Picked file: $fileName (${file.size} bytes)"); - // if (!fileName.toLowerCase().endsWith('.pdf')) { - // ToastHelper.showErrorToast(context, Only PDF files are allowed.) - // ScaffoldMessenger.of(context).showSnackBar( - // const SnackBar( - // content: Text('Only PDF files are allowed.'), - // backgroundColor: Colors.red, - // ), - // ); - // return; // Stop here - // } + if (!fileName.toLowerCase().endsWith('.pdf')) { + ToastHelper.showErrorToast( + context, + 'Only PDF files are allowed.', + ); + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text('Only PDF files are allowed.'), + backgroundColor: Colors.red, + ), + ); + return; // Stop here + } print("Picked file: $fileName (${file.size} bytes)"); setState(() { docPDFUploadedFile = file; }); - handleUploadPolicy(); + + if (enqBrokerName == 'Nhance') { + handleUploadPolicy(); + } }, ), if (docUploadedPDFFileUrlFromApi != null) ...[ - const SizedBox(width: 10), + const SizedBox(width: 5), Tooltip( message: 'Download', // color: Colors.white, @@ -1538,6 +1750,414 @@ class PolicyStaffTabState extends ConsumerState { } } + // NEW FIELDs + + Widget buildThirdParty(BuildContext context) { + return Row( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + SizedBox( + width: MediaQuery.of(context).size.width * 0.15, + child: Text('Third Party *', style: _textStyle), + ), + SizedBox(width: 20), + ThemedFormField( + controller: controllers['tp']!, + backgroundColor: Color(0xffEDF6F5), + errorBorderColor: Color(0xffEDF6F5), + hintText: '0', + validator: (value) => Validators.requiredField(value, "Third Party"), + inputFormatters: [ + FilteringTextInputFormatter.allow(RegExp(r'[.0-9]')), + ], + // onChanged: (value) => calculateAmount(), + onChanged: (v) { + print('pa onChanged -> $v'); // should show on every keystroke + calculateAmount(); + }, + txtwidth: ResponsiveLayout.isMobile(context) + ? null + : MediaQuery.of(context).size.width * 0.14, + ), + ], + ); + } + + Widget buildOwnDamage(BuildContext context) { + return Row( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + SizedBox( + width: MediaQuery.of(context).size.width * 0.15, + child: Text('Own Damage *', style: _textStyle), + ), + SizedBox(width: 20), + ThemedFormField( + controller: controllers['od']!, + backgroundColor: Color(0xffEDF6F5), + errorBorderColor: Color(0xffEDF6F5), + hintText: '0', + validator: (value) => Validators.requiredField(value, "OwnDamage"), + onChanged: (value) => calculateAmount(), + inputFormatters: [ + FilteringTextInputFormatter.allow(RegExp(r'[.0-9]')), + ], + txtwidth: ResponsiveLayout.isMobile(context) + ? null + : MediaQuery.of(context).size.width * 0.14, + ), + ], + ); + } + + Widget buildPersonalAccident(BuildContext context) { + return Row( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + SizedBox( + width: MediaQuery.of(context).size.width * 0.15, + child: Text('Personal Accident*', style: _textStyle), + ), + SizedBox(width: 20), + ThemedFormField( + controller: controllers['pa']!, + backgroundColor: Color(0xffEDF6F5), + errorBorderColor: Color(0xffEDF6F5), + hintText: '0', + onChanged: (value) => calculateAmount(), + validator: (value) => + Validators.requiredField(value, "PersonalAccident"), + inputFormatters: [ + FilteringTextInputFormatter.allow(RegExp(r'[.0-9]')), + ], + txtwidth: ResponsiveLayout.isMobile(context) + ? null + : MediaQuery.of(context).size.width * 0.14, + ), + ], + ); + } + + Widget buildCGST(BuildContext context) { + return Row( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + SizedBox( + width: MediaQuery.of(context).size.width * 0.15, + child: Text('CGST *', style: _textStyle), + ), + SizedBox(width: 20), + ThemedFormField( + controller: controllers['cgst']!, + backgroundColor: Color(0xffEDF6F5), + errorBorderColor: Color(0xffEDF6F5), + hintText: '0', + validator: (value) => Validators.requiredField(value, "CGST"), + inputFormatters: [ + FilteringTextInputFormatter.allow(RegExp(r'[.0-9]')), + ], + txtwidth: ResponsiveLayout.isMobile(context) + ? null + : MediaQuery.of(context).size.width * 0.14, + ), + ], + ); + } + + Widget buildSGST(BuildContext context) { + return Row( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + SizedBox( + width: MediaQuery.of(context).size.width * 0.15, + child: Text('SGST *', style: _textStyle), + ), + SizedBox(width: 20), + ThemedFormField( + controller: controllers['sgst']!, + backgroundColor: Color(0xffEDF6F5), + errorBorderColor: Color(0xffEDF6F5), + hintText: '0', + inputFormatters: [ + FilteringTextInputFormatter.allow(RegExp(r'[.0-9]')), + ], + validator: (value) => Validators.requiredField(value, "SGST"), + txtwidth: ResponsiveLayout.isMobile(context) + ? null + : MediaQuery.of(context).size.width * 0.14, + ), + ], + ); + } + + Widget buildIGST(BuildContext context) { + return Row( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + SizedBox( + width: MediaQuery.of(context).size.width * 0.15, + child: Text('IGST *', style: _textStyle), + ), + SizedBox(width: 20), + ThemedFormField( + controller: controllers['igst']!, + backgroundColor: Color(0xffEDF6F5), + errorBorderColor: Color(0xffEDF6F5), + hintText: '0', + validator: (value) => Validators.requiredField(value, "IGST"), + inputFormatters: [ + FilteringTextInputFormatter.allow(RegExp(r'[.0-9]')), + ], + txtwidth: ResponsiveLayout.isMobile(context) + ? null + : MediaQuery.of(context).size.width * 0.14, + ), + ], + ); + } + + Widget buildPremiumTotal(BuildContext context) { + return Row( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + SizedBox( + width: MediaQuery.of(context).size.width * 0.15, + child: Text('Total Premium *', style: _textStyle), + ), + SizedBox(width: 20), + ThemedFormField( + controller: controllers['premiumTOT']!, + backgroundColor: Color(0xffEDF6F5), + errorBorderColor: Color(0xffEDF6F5), + hintText: '0', + validator: (value) => Validators.requiredField(value, "premium"), + inputFormatters: [ + FilteringTextInputFormatter.allow(RegExp(r'[.0-9]')), + ], + txtwidth: ResponsiveLayout.isMobile(context) + ? null + : MediaQuery.of(context).size.width * 0.14, + ), + ], + ); + } + + Widget buildRTOStateCode(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('RTO State Code *', style: _textStyle), + SizedBox(height: 10), + ThemedFormField( + controller: controllers['rto_state_code']!, + validator: (value) => + Validators.requiredField(value, "RTO State Code"), + txtwidth: ResponsiveLayout.isMobile(context) + ? null + : MediaQuery.of(context).size.width * 0.26, + ), + ], + ); + } + + Widget buildRTOCityCode(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('RTO City Code *', style: _textStyle), + SizedBox(height: 10), + ThemedFormField( + controller: controllers['rto_city_code']!, + validator: (value) => + Validators.requiredField(value, "RTO City Code"), + txtwidth: ResponsiveLayout.isMobile(context) + ? null + : MediaQuery.of(context).size.width * 0.26, + ), + ], + ); + } + + Widget buildWeight(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('Weight *', style: _textStyle), + SizedBox(height: 10), + ThemedFormField( + controller: controllers['weight']!, + validator: (value) => Validators.requiredField(value, "weight"), + txtwidth: ResponsiveLayout.isMobile(context) + ? null + : MediaQuery.of(context).size.width * 0.26, + ), + ], + ); + } + + Widget buildFuelType(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('Fuel Type *', style: _textStyle), + SizedBox(height: 10), + ThemedFormField( + controller: controllers['fuel_type']!, + validator: (value) => Validators.requiredField(value, "Fuel Type"), + txtwidth: ResponsiveLayout.isMobile(context) + ? null + : MediaQuery.of(context).size.width * 0.26, + ), + ], + ); + } + + Widget buildDateOfRegistration(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('Date Of Registration *', style: _textStyle), + SizedBox(height: 10), + ThemedFormField( + controller: controllers['date_of_registration']!, + validator: (value) => + Validators.requiredField(value, "Date Of Registration"), + txtwidth: ResponsiveLayout.isMobile(context) + ? null + : MediaQuery.of(context).size.width * 0.26, + ), + ], + ); + } + + Widget buildYearOfManufacture(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('Year Of Manufacture *', style: _textStyle), + SizedBox(height: 10), + ThemedFormField( + controller: controllers['year_of_manufacture']!, + validator: (value) => + Validators.requiredField(value, "Year Of Manufacture"), + txtwidth: ResponsiveLayout.isMobile(context) + ? null + : MediaQuery.of(context).size.width * 0.26, + ), + ], + ); + } + + Widget buildEngineNo(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('Engine No *', style: _textStyle), + SizedBox(height: 10), + ThemedFormField( + controller: controllers['engine_no']!, + validator: (value) => Validators.requiredField(value, "Engine No"), + txtwidth: ResponsiveLayout.isMobile(context) + ? null + : MediaQuery.of(context).size.width * 0.26, + ), + ], + ); + } + + Widget buildChassisNo(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('Chassis No *', style: _textStyle), + SizedBox(height: 10), + ThemedFormField( + controller: controllers['chassis_no']!, + validator: (value) => Validators.requiredField(value, "Chassis No"), + txtwidth: ResponsiveLayout.isMobile(context) + ? null + : MediaQuery.of(context).size.width * 0.26, + ), + ], + ); + } + + Widget buildMake(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('Make *', style: _textStyle), + SizedBox(height: 10), + ThemedFormField( + controller: controllers['make']!, + validator: (value) => Validators.requiredField(value, "Make"), + txtwidth: ResponsiveLayout.isMobile(context) + ? null + : MediaQuery.of(context).size.width * 0.26, + ), + ], + ); + } + + Widget buildModel(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('Model *', style: _textStyle), + SizedBox(height: 10), + ThemedFormField( + controller: controllers['model']!, + validator: (value) => Validators.requiredField(value, "Model"), + txtwidth: ResponsiveLayout.isMobile(context) + ? null + : MediaQuery.of(context).size.width * 0.26, + ), + ], + ); + } + + Widget buildCubicCapacity(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('Cubic Capacity *', style: _textStyle), + SizedBox(height: 10), + ThemedFormField( + controller: controllers['cubic_capacity']!, + validator: (value) => + Validators.requiredField(value, "Cubic Capacity"), + txtwidth: ResponsiveLayout.isMobile(context) + ? null + : MediaQuery.of(context).size.width * 0.26, + ), + ], + ); + } + + Widget buildVehicleType(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('Vehicle Type *', style: _textStyle), + SizedBox(height: 10), + ThemedFormField( + controller: controllers['vehicle_type']!, + validator: (value) => Validators.requiredField(value, "Vehicle Type"), + txtwidth: ResponsiveLayout.isMobile(context) + ? null + : MediaQuery.of(context).size.width * 0.26, + ), + ], + ); + } + Widget buildResponsiveField({required String label, required Widget field}) { final isMobile = ResponsiveLayout.isMobile(context); diff --git a/lib/presentation/screens/staff/Enquiry/tabs/quotationTab.dart b/lib/presentation/screens/staff/Enquiry/tabs/quotationTab.dart index 3e3a5f5..3faaa32 100644 --- a/lib/presentation/screens/staff/Enquiry/tabs/quotationTab.dart +++ b/lib/presentation/screens/staff/Enquiry/tabs/quotationTab.dart @@ -165,6 +165,7 @@ class QuotationStaffTabState extends ConsumerState { void updateEnquiryData(enquiryData) { print('updateEnquiryData - $enquiryData'); + if (!mounted) return; setState(() { selectedInsurdId = enquiryData['insurer_id']; selectedEnquiryId = enquiryData['id']; @@ -192,13 +193,14 @@ class QuotationStaffTabState extends ConsumerState { Future getQuotationList(enqQuotation) async { print('getClaimList called enqQuotation- $enqQuotation'); + if (!mounted) return; setState(() { isLoading = true; }); try { final response = await apiService.findEnqQuotePolicyView(enqQuotation); - + if (!mounted) return; if (response['status'] == 'success') { print('quoationListData - ${response['data']}'); setState(() { @@ -226,6 +228,7 @@ class QuotationStaffTabState extends ConsumerState { } catch (e) { print('Exception occurred: $e'); } finally { + if (!mounted) return; setState(() { isLoading = false; }); @@ -266,7 +269,8 @@ class QuotationStaffTabState extends ConsumerState { // scrollDirection: Axis.vertical, child: Column( children: [ - if (!blockKey && roleId != 'manager') ...[ + if (!blockKey) ...[ + // if (!blockKey && roleId != 'manager') ...[ CreateQuotationForm( key: ValueKey(selectedQuotationFrmListId ?? "new"), userId: userId, diff --git a/lib/presentation/screens/staff/Enquiry/tabs/tab.dart b/lib/presentation/screens/staff/Enquiry/tabs/tab.dart index 82c52c4..0674262 100644 --- a/lib/presentation/screens/staff/Enquiry/tabs/tab.dart +++ b/lib/presentation/screens/staff/Enquiry/tabs/tab.dart @@ -39,6 +39,7 @@ class TabEnquiryStaffListState extends ConsumerState { dynamic selectedInsuredName; dynamic selectedVehicleNum; dynamic selectedVehicleType; + dynamic selectedBrokerName; @override void initState() { @@ -97,8 +98,9 @@ class TabEnquiryStaffListState extends ConsumerState { selectedInsuredName = data['enquiry']?['name']; selectedVehicleNum = data['enquiry']?['reg_no']; selectedVehicleType = data['enquiry']?['vehicle_type']; + selectedBrokerName = data['enquiry']?['broker_name']; print( - 'loadQuotationTab 4s - $selectedInsuredName - $selectedVehicleNum - $selectedVehicleType', + 'loadQuotationTab 4s -$selectedBrokerName $selectedInsuredName - $selectedVehicleNum - $selectedVehicleType', ); tabs = [ @@ -126,6 +128,24 @@ class TabEnquiryStaffListState extends ConsumerState { expandedIndex = selectedIndex; }); + + if (selectedBrokerName == 'Nhance') { + print("Chooese name Nhance"); + + final prefs = await SharedPreferences.getInstance(); + await prefs.remove('enqBrokerNameProvider'); + + // ✅ Save the new id + await prefs.setString( + 'enqBrokerNameProvider', + selectedBrokerName.toString(), + ); + ref.read(enqBrokerNameProvider.notifier).state = selectedBrokerName; + } else { + final prefs = await SharedPreferences.getInstance(); + await prefs.remove('enqBrokerNameProvider'); + ref.read(enqBrokerNameProvider.notifier).state = null; + } } /// Public method to load data and select a tab @@ -146,9 +166,8 @@ class TabEnquiryStaffListState extends ConsumerState { return AlertDialog( backgroundColor: Colors.white, content: Container( - width: MediaQuery.of(context).size.width * 0.6, - height: MediaQuery.of(context).size.height * 0.8, - + width: MediaQuery.of(context).size.width * 0.65, + height: MediaQuery.of(context).size.height * 0.9, child: isLoading ? const Center(child: CircularProgressIndicator()) : tabs.isEmpty @@ -211,6 +230,7 @@ class TabEnquiryStaffListState extends ConsumerState { insured_name(context), vehcile_num(context), vehcile_Type(context), + broker(context), SizedBox.shrink(), ], ), @@ -310,6 +330,24 @@ class TabEnquiryStaffListState extends ConsumerState { ); } + Widget broker(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('Broker', style: _textStyle), + SizedBox(height: 10), + Text(selectedBrokerName ?? '', style: _textDataStyle), + // ThemedFormField( + // controller: controllers['insurer']!, + // readOnly: true, + // txtwidth: ResponsiveLayout.isMobile(context) + // ? null + // : MediaQuery.of(context).size.width * 0.2, + // ), + ], + ); + } + static final _textStyle = GoogleFonts.inter( fontSize: 12, fontWeight: FontWeight.w500, diff --git a/lib/presentation/screens/staff/assignStaff.dart b/lib/presentation/screens/staff/assignStaff.dart index 0418d4e..da191ff 100644 --- a/lib/presentation/screens/staff/assignStaff.dart +++ b/lib/presentation/screens/staff/assignStaff.dart @@ -51,19 +51,26 @@ class _AddDialogState extends ConsumerState { List> getInsurersData = []; List> filteredInsurersData = []; String? selectedInsurer; + List> getBrokerData = []; + List> filteredBrokerData = []; + String? selectedBroker; final _formKey = GlobalKey(); final _formKeyEndrosment = GlobalKey(); final GlobalKey>> dropDownKey = GlobalKey>>(); - final GlobalKey>> - dropDownKeyInsurer = GlobalKey>>(); - List tabHeader = ['regNum']; - List> getStaffDetailsData = []; - List> filteredStaffData = []; + final GlobalKey>> + dropDownKeyInsurerEnqAsgn = + GlobalKey>>(); + + final GlobalKey>> dropDownKeyBroker = + GlobalKey>>(); + + List> getStaffDetailsDataEnqAsgn = []; + List> filteredStaffDataEnqAsgn = []; String? selectedStaff; String? selectedRegNum; @@ -74,6 +81,7 @@ class _AddDialogState extends ConsumerState { "id": widget.enquiryPrimaryId, "assigned_to": selectedStaff, "insurer_id": selectedInsurer, + "broker_id": selectedBroker, "updated_by": widget.userId, }; return data; @@ -92,17 +100,19 @@ class _AddDialogState extends ConsumerState { // 🔹 Init logic here (API calls, token fetch, etc.) _initializeToken(); - // getStaffDetails(1); + // getStaffDetailsForEnquiryAssignment(1); getInsurers(); + getBroker(); Future.microtask(() { final managerId = ref.watch(managerIdProvider); - final userID = ref.watch(userIdProvider); // final handlerId = ref.watch(handlerIdProvider); role = ref.watch(userRoleProvider); + final userID = ref.watch(userIdProvider); + print("managerId - $managerId"); if (userID != null) { print('hansles'); - getStaffDetails(userID); + getStaffDetailsForEnquiryAssignment(userID); } }); } @@ -143,8 +153,39 @@ class _AddDialogState extends ConsumerState { } } - Future getStaffDetails(int id) async { - print('getStaffDetails called By handler'); + Future getBroker() async { + print('getBroker called'); + setState(() { + isLoading = true; + }); + + try { + final response = await apiService.fetchMasterDropDown('Broker'); + + if (response['status'] == 200) { + print('getBroker - ${response['data']}'); + setState(() { + getBrokerData = List>.from(response['data']); + print('API Data - $getBrokerData'); + + filteredBrokerData = List.from(getBrokerData); + print('originalData - $filteredBrokerData'); + }); + } else { + getBrokerData = []; + filteredBrokerData = []; + } + } catch (e) { + print('Exception occurred: $e'); + } finally { + setState(() { + isLoading = false; + }); + } + } + + Future getStaffDetailsForEnquiryAssignment(int id) async { + print('getStaffDetailsForEnquiryAssignment called By handler'); setState(() { isLoading = true; }); @@ -157,19 +198,19 @@ class _AddDialogState extends ConsumerState { ); if (response['status'] == 'success') { - print('getStaffDetails - ${response['data']}'); + print('getStaffDetailsForEnquiryAssignment - ${response['data']}'); setState(() { - getStaffDetailsData = List>.from( + getStaffDetailsDataEnqAsgn = List>.from( response['data'], ); - print('API Data - $getStaffDetailsData'); + print('API Data - $getStaffDetailsDataEnqAsgn'); - filteredStaffData = List.from(getStaffDetailsData); - print('originalData - $filteredStaffData'); + filteredStaffDataEnqAsgn = List.from(getStaffDetailsDataEnqAsgn); + print('originalData - $filteredStaffDataEnqAsgn'); }); } else { - getStaffDetailsData = []; - filteredStaffData = []; + getStaffDetailsDataEnqAsgn = []; + filteredStaffDataEnqAsgn = []; } } catch (e) { print('Exception occurred: $e'); @@ -363,6 +404,7 @@ class _AddDialogState extends ConsumerState { buildRegistrationNumber(context), buildSelectStaffMem(context), buildInsurer(context), + buildBroker(context), ], ), ); @@ -409,14 +451,14 @@ class _AddDialogState extends ConsumerState { } Widget buildSelectStaffMem(ctx) { - // Map? selectedVehicle = filteredStaffData.firstWhere( + // Map? selectedVehicle = filteredStaffDataEnqAsgn.firstWhere( // (item) => item['id'].toString() == selectedStaff, // orElse: () => {}, // ); Map? selectedVehicle; try { - selectedVehicle = filteredStaffData.firstWhere( + selectedVehicle = filteredStaffDataEnqAsgn.firstWhere( (item) => item['id'].toString() == selectedStaff, ); } catch (e) { @@ -439,7 +481,7 @@ class _AddDialogState extends ConsumerState { // selectedItem: selectedVehicle.isNotEmpty ? selectedVehicle : null, selectedItem: selectedVehicle, items: (filter, infiniteScrollProps) { - return filteredStaffData; + return filteredStaffDataEnqAsgn; }, itemAsString: (val) => val['name'].toString(), @@ -490,6 +532,19 @@ class _AddDialogState extends ConsumerState { ), ), // constraints: BoxConstraints(), + itemBuilder: (context, item, isDisabled, isSelected) { + return Container( + // color: isSelected ? Colors.blue.withOpacity(0.1) : null, + padding: const EdgeInsets.symmetric( + horizontal: 8, + vertical: 3, + ), + child: Text( + item['name'].toString(), + style: GoogleFonts.inter(fontSize: 14, color: Colors.black), + ), + ); + }, ), onChanged: (val) { @@ -528,7 +583,7 @@ class _AddDialogState extends ConsumerState { : MediaQuery.of(context).size.width * 0.26, // height: 40, child: DropdownSearch>( - key: dropDownKeyInsurer, + key: dropDownKeyInsurerEnqAsgn, selectedItem: selectedInsurerd.isNotEmpty ? selectedInsurerd : null, items: (filter, infiniteScrollProps) { return filteredInsurersData; @@ -581,6 +636,19 @@ class _AddDialogState extends ConsumerState { ), ), ), + itemBuilder: (context, item, isDisabled, isSelected) { + return Container( + // color: isSelected ? Colors.blue.withOpacity(0.1) : null, + padding: const EdgeInsets.symmetric( + horizontal: 8, + vertical: 3, + ), + child: Text( + item['name'].toString(), + style: GoogleFonts.inter(fontSize: 14, color: Colors.black), + ), + ); + }, // constraints: BoxConstraints(), ), @@ -599,6 +667,111 @@ class _AddDialogState extends ConsumerState { ); } + Widget buildBroker(BuildContext context) { + Map? selectedBrokers = filteredBrokerData.firstWhere( + (item) => item['id'].toString() == selectedBroker, + orElse: () => {}, + ); + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('Broker *', style: _textStyle), + SizedBox(height: 10), + Container( + decoration: BoxDecoration( + color: Colors.white, + // borderRadius: BorderRadius.circular(10.0), + ), + width: ResponsiveLayout.isMobile(context) + ? null + : MediaQuery.of(context).size.width * 0.26, + // height: 40, + child: DropdownSearch>( + key: dropDownKeyBroker, + selectedItem: selectedBrokers.isNotEmpty ? selectedBrokers : null, + items: (filter, infiniteScrollProps) { + return filteredBrokerData; + }, + + itemAsString: (val) => val['name'].toString(), // what to show + compareFn: (item, selectedItem) => + item['id'] == selectedItem['id'], // ✅ compare by id + validator: (val) { + if (val == null) { + return "Required"; // ✅ error message + } + return null; + }, + decoratorProps: DropDownDecoratorProps( + decoration: + AppInputDecorations.dropdownDecoration( + label: "Select Broker", + ).copyWith( + filled: true, + fillColor: Color( + 0xFFEDF6F5, + ), // 👈 makes the dropdown input white + ), + ), + + popupProps: PopupProps.menu( + fit: FlexFit.loose, + constraints: BoxConstraints(maxHeight: 250), + menuProps: MenuProps( + backgroundColor: + Colors.white, // 👈 sets dropdown background to white + ), + showSearchBox: true, + searchFieldProps: TextFieldProps( + decoration: InputDecoration( + filled: true, + fillColor: Colors.white, + hintText: "Search Broker...", + enabledBorder: OutlineInputBorder( + borderSide: BorderSide( + color: Colors.white, + ), // 👈 Normal border + ), + focusedBorder: OutlineInputBorder( + borderSide: BorderSide( + color: Colors.white, + width: 1.5, + ), // 👈 Focused border + ), + ), + ), + + itemBuilder: (context, item, isDisabled, isSelected) { + return Container( + // color: isSelected ? Colors.blue.withOpacity(0.1) : null, + padding: const EdgeInsets.symmetric( + horizontal: 8, + vertical: 3, + ), + child: Text( + item['name'].toString(), + style: GoogleFonts.inter(fontSize: 14, color: Colors.black), + ), + ); + }, + // constraints: BoxConstraints(), + ), + + onChanged: (val) { + if (val != null) { + print("Selected Broker : ${val['name']}"); + print("Id: ${val['id']}"); + selectedBroker = val['id']; + // controllers['agentId']?.text = val['agent_code']; + // agentId = agent['id']; + } + }, + ), + ), + ], + ); + } + // ------------------- STyle --------------------------------- static final TextStyle _textStyle = TextStyle( diff --git a/lib/presentation/themes/indicators/customizd_file_upload.dart b/lib/presentation/themes/indicators/customizd_file_upload.dart index 43b0dca..c5148d9 100644 --- a/lib/presentation/themes/indicators/customizd_file_upload.dart +++ b/lib/presentation/themes/indicators/customizd_file_upload.dart @@ -1,8 +1,157 @@ import 'package:flutter/material.dart'; import 'package:file_picker/file_picker.dart'; +import 'package:google_fonts/google_fonts.dart'; import 'fileUploadService.dart'; +class ThemedUploadField extends StatefulWidget { + final String? hintText; + final double? txtwidth; + final double? txtheight; + final Color? backgroundColor; + final Color? borderColor; + final Color? errorBorderColor; + final Function(String fileName, PlatformFile file)? onFileSelected; + FormFieldValidator? validator; + final List? allowedExtensions; + final bool isTxtBtnCase; + final String? txtName; + final double? padVertical; + final double? padHorizontal; + + ThemedUploadField({ + super.key, + this.hintText, + this.txtwidth, + this.txtheight, + this.backgroundColor, + this.borderColor, + this.errorBorderColor, + this.onFileSelected, + this.validator, + this.allowedExtensions, + this.isTxtBtnCase = false, + this.txtName, + this.padVertical, + this.padHorizontal, + }); + + @override + State createState() => _ThemedUploadFieldState(); +} + +class _ThemedUploadFieldState extends State { + final fileService = FileUploadService(); + String? selectedFileName; + String? errorMessage; + + Future _pickFile() async { + final error = await fileService.pickSingleFile( + maxFileSizeInMB: 3, + allowedExtensions: widget.allowedExtensions, + ); + if (error != null) { + setState(() => errorMessage = error); + ScaffoldMessenger.of( + context, + ).showSnackBar(SnackBar(content: Text(error))); + } else if (fileService.singleFile != null) { + setState(() { + selectedFileName = fileService.singleFile!.name; + errorMessage = null; + }); + + widget.onFileSelected?.call( + fileService.singleFile!.name, + fileService.singleFile!, + ); + } + } + + @override + Widget build(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + width: widget.txtwidth ?? MediaQuery.of(context).size.width, + height: widget.txtheight, + child: DecoratedBox( + decoration: BoxDecoration(borderRadius: BorderRadius.circular(10)), + child: InkWell( + onTap: _pickFile, + + borderRadius: BorderRadius.circular(10), + child: Container( + padding: EdgeInsets.symmetric( + vertical: widget.padVertical ?? 10, + horizontal: widget.padHorizontal ?? 15, + ), + decoration: BoxDecoration( + color: widget.backgroundColor ?? Colors.white, + borderRadius: BorderRadius.circular(10), + border: Border.all( + color: errorMessage != null + ? (widget.errorBorderColor ?? Colors.red) + : (widget.borderColor ?? + widget.backgroundColor ?? + Colors.grey.shade50), + ), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + if (!widget.isTxtBtnCase) ...[ + Expanded( + child: Text( + selectedFileName ?? + widget.hintText ?? + "Upload Document", + style: TextStyle( + fontSize: 12, + color: errorMessage != null + ? Colors.red + : Colors.black, + ), + overflow: TextOverflow.ellipsis, + ), + ), + const Icon( + Icons.file_upload_outlined, + color: Colors.black, + ), + ], + + if (widget.isTxtBtnCase) ...[ + Center( + child: Tooltip( + message: 'Upload Documents', + child: Text( + widget.txtName!, + style: GoogleFonts.inter(fontSize: 12), + ), + ), + ), + ], + ], + ), + ), + ), + ), + ), + if (errorMessage != null) + Padding( + padding: const EdgeInsets.only(left: 8, top: 4), + child: Text( + errorMessage!, + style: const TextStyle(color: Colors.red, fontSize: 10), + ), + ), + ], + ); + } +} + // class ThemedUploadField extends FormField { // ThemedUploadField({ // Key? key, @@ -98,124 +247,3 @@ import 'fileUploadService.dart'; // }, // ); // } - -class ThemedUploadField extends StatefulWidget { - final String? hintText; - final double? txtwidth; - final double? txtheight; - final Color? backgroundColor; - final Color? borderColor; - final Color? errorBorderColor; - final Function(String fileName, PlatformFile file)? onFileSelected; - FormFieldValidator? validator; - final List? allowedExtensions; - - ThemedUploadField({ - super.key, - this.hintText, - this.txtwidth, - this.txtheight, - this.backgroundColor, - this.borderColor, - this.errorBorderColor, - this.onFileSelected, - this.validator, - this.allowedExtensions, - }); - - @override - State createState() => _ThemedUploadFieldState(); -} - -class _ThemedUploadFieldState extends State { - final fileService = FileUploadService(); - String? selectedFileName; - String? errorMessage; - - Future _pickFile() async { - final error = await fileService.pickSingleFile( - maxFileSizeInMB: 3, - allowedExtensions: widget.allowedExtensions, - ); - if (error != null) { - setState(() => errorMessage = error); - ScaffoldMessenger.of( - context, - ).showSnackBar(SnackBar(content: Text(error))); - } else if (fileService.singleFile != null) { - setState(() { - selectedFileName = fileService.singleFile!.name; - errorMessage = null; - }); - - widget.onFileSelected?.call( - fileService.singleFile!.name, - fileService.singleFile!, - ); - } - } - - @override - Widget build(BuildContext context) { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Container( - width: widget.txtwidth ?? MediaQuery.of(context).size.width, - height: widget.txtheight, - child: DecoratedBox( - decoration: BoxDecoration(borderRadius: BorderRadius.circular(10)), - child: InkWell( - onTap: _pickFile, - - borderRadius: BorderRadius.circular(10), - child: Container( - padding: const EdgeInsets.symmetric( - vertical: 10, - horizontal: 15, - ), - decoration: BoxDecoration( - color: widget.backgroundColor ?? Colors.white, - borderRadius: BorderRadius.circular(10), - border: Border.all( - color: errorMessage != null - ? (widget.errorBorderColor ?? Colors.red) - : (widget.borderColor ?? Colors.grey.shade50), - ), - ), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Expanded( - child: Text( - selectedFileName ?? - widget.hintText ?? - "Upload Document", - style: TextStyle( - fontSize: 12, - color: errorMessage != null - ? Colors.red - : Colors.black, - ), - overflow: TextOverflow.ellipsis, - ), - ), - const Icon(Icons.file_upload_outlined, color: Colors.black), - ], - ), - ), - ), - ), - ), - if (errorMessage != null) - Padding( - padding: const EdgeInsets.only(left: 8, top: 4), - child: Text( - errorMessage!, - style: const TextStyle(color: Colors.red, fontSize: 10), - ), - ), - ], - ); - } -} diff --git a/lib/presentation/themes/indicators/export_btn.dart b/lib/presentation/themes/indicators/export_btn.dart index d6e7f88..25e5c54 100644 --- a/lib/presentation/themes/indicators/export_btn.dart +++ b/lib/presentation/themes/indicators/export_btn.dart @@ -80,21 +80,21 @@ class ExportBtn extends HookWidget { child: Row( mainAxisSize: MainAxisSize.min, children: [ - ?txt! - ? Text( - 'Export', - style: TextStyle( - color: Colors.white, - fontWeight: FontWeight.w600, - fontSize: 14, - ), - ) - : null, - ?txt! ? SizedBox(width: 15) : null, + // ?txt! + // ? Text( + // 'Export', + // style: TextStyle( + // color: Colors.white, + // fontWeight: FontWeight.w600, + // fontSize: 14, + // ), + // ) + // : null, + // ?txt! ? SizedBox(width: 15) : null, Image.asset( "assets/miscellaneous/export.png", - height: 25, - width: 25, + height: 15, + width: 15, ), // Icon(Icons.input_sharp, color: Colors.white), // Image.asset("assets/miscellaneous/export", height: 15, width: 15), diff --git a/lib/presentation/themes/indicators/input_field_decoration.dart b/lib/presentation/themes/indicators/input_field_decoration.dart index b403f38..c920976 100644 --- a/lib/presentation/themes/indicators/input_field_decoration.dart +++ b/lib/presentation/themes/indicators/input_field_decoration.dart @@ -8,7 +8,8 @@ class AppInputDecorations { hintText: hint, filled: true, fillColor: const Color(0xFFECECEC), - contentPadding: const EdgeInsets.symmetric(vertical: 14, horizontal: 15), + + contentPadding: const EdgeInsets.symmetric(vertical: 14, horizontal: 1), border: OutlineInputBorder( borderRadius: BorderRadius.circular(10), borderSide: const BorderSide(color: Color(0xFFFFFFFF)), @@ -16,7 +17,8 @@ class AppInputDecorations { errorStyle: const TextStyle(color: Colors.red, fontSize: 12), enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(10), - borderSide: const BorderSide(color: Color(0xFFFFFFFF)), + + borderSide: const BorderSide(color: Color(0xFFFFFFFF), width: 1), ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(10), diff --git a/lib/presentation/themes/indicators/text_field_theme.dart b/lib/presentation/themes/indicators/text_field_theme.dart index 070cbc1..cb2ab17 100644 --- a/lib/presentation/themes/indicators/text_field_theme.dart +++ b/lib/presentation/themes/indicators/text_field_theme.dart @@ -6,9 +6,10 @@ import 'package:google_fonts/google_fonts.dart'; // import 'package:uae_stat/config/my_theme.dart'; class ThemedFormField extends HookWidget { - const ThemedFormField({ + ThemedFormField({ super.key, this.hintText, + this.imgPath, this.txtwidth, this.txtheight, @@ -21,13 +22,22 @@ class ThemedFormField extends HookWidget { this.errorBorderColor, this.errorTextColor, this.readOnly = false, // ✅ new + this.isdense = false, // ✅ new this.keyboardType, // ✅ new this.maxLength, // ✅ new this.inputFormatters, this.isShowCursor, + this.onChanged, + this.verticalPad, + this.horizonalPad, + this.enableBorderWidth, }); + final double? verticalPad; + final double? horizonalPad; + final double? enableBorderWidth; final String? hintText; + final String? imgPath; final String? Function(String? text)? validator; final TextEditingController controller; @@ -39,11 +49,15 @@ class ThemedFormField extends HookWidget { final Color? errorTextColor; final double? txtwidth; final bool readOnly; + final bool isdense; + final bool? isShowCursor; final TextInputType? keyboardType; final int? maxLength; final double? txtheight; final List? inputFormatters; + // void Function(dynamic value)? onChanged; + final ValueChanged? onChanged; @override Widget build(BuildContext context) { @@ -70,34 +84,49 @@ class ThemedFormField extends HookWidget { ), ); final inputDecoration = InputDecoration( - // isDense: true, - errorStyle: TextStyle(color: const Color(0xFFD83731)), + isDense: isdense, + errorStyle: TextStyle(color: Color(0xFFD83731)), errorBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(10), - borderSide: BorderSide(color: const Color(0xFFD83731), width: 1), + borderSide: BorderSide( + color: errorBorderColor ?? Color(0xFFD83731), + width: 1, + ), ), focusedErrorBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(10), - borderSide: BorderSide(color: const Color(0xFFD83731), width: 1), + borderSide: BorderSide( + color: errorBorderColor ?? Color(0xFFD83731), + width: 1, + ), ), filled: true, fillColor: backgroundColor ?? Colors.white, - contentPadding: const EdgeInsets.symmetric(vertical: 14, horizontal: 15), + contentPadding: EdgeInsets.symmetric( + vertical: verticalPad ?? 14, + horizontal: horizonalPad ?? 15, + ), border: OutlineInputBorder( borderRadius: BorderRadius.circular(10), // borderSide: BorderSide(color: Color(0xFF50A398)), - borderSide: BorderSide(color: Color(0xFFFFFFFF)), + borderSide: BorderSide( + color: borderColor ?? Color(0xFFFFFFFF), + width: enableBorderWidth ?? 1, + ), ), enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(10), // borderSide: BorderSide(color: Color(0xFF50A398)), - borderSide: BorderSide(color: Color(0xFFFFFFFF)), + borderSide: BorderSide( + color: borderColor ?? backgroundColor ?? Color(0xFFFFFFFF), + width: enableBorderWidth ?? 1, + ), ), focusedBorder: readOnly ? OutlineInputBorder( borderRadius: BorderRadius.circular(10), // borderSide: BorderSide(color: Color(0xFF50A398)), - borderSide: BorderSide(color: Color(0xFFFFFFFF)), + borderSide: BorderSide(color: borderColor ?? Color(0xFFFFFFFF)), ) : OutlineInputBorder( borderRadius: BorderRadius.circular(10), @@ -135,6 +164,7 @@ class ThemedFormField extends HookWidget { ); return Container( width: txtwidth ?? MediaQuery.of(context).size.width, + height: txtheight ?? null, child: DecoratedBox( decoration: boxDecoration, @@ -155,7 +185,7 @@ class ThemedFormField extends HookWidget { minLines: (keyboardType == TextInputType.multiline) ? 3 : 1, maxLines: (keyboardType == TextInputType.multiline) ? null : 1, style: GoogleFonts.inter( - fontSize: 14, // 👈 Change this to your desired size + fontSize: 12, // 👈 Change this to your desired size fontWeight: FontWeight.w400, // optional color: Colors.black, // optional ), diff --git a/lib/presentation/themes/indicators/text_field_theme_inline_editor.dart b/lib/presentation/themes/indicators/text_field_theme_inline_editor.dart new file mode 100644 index 0000000..1f81a87 --- /dev/null +++ b/lib/presentation/themes/indicators/text_field_theme_inline_editor.dart @@ -0,0 +1,199 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:google_fonts/google_fonts.dart'; + +// import 'package:uae_stat/config/my_theme.dart'; + +class ThemedFormInlineField extends HookWidget { + ThemedFormInlineField({ + super.key, + this.hintText, + this.widthNone = false, + this.imgPath, + this.txtwidth, + this.txtheight, + required this.controller, + this.validator, + this.isObscurable = false, + this.hintColor, + this.backgroundColor, + this.borderColor, + this.errorBorderColor, + this.errorTextColor, + this.readOnly = false, // ✅ new + this.keyboardType, // ✅ new + this.maxLength, // ✅ new + this.inputFormatters, + this.isShowCursor, + this.onChanged, + this.isdense, + }); + + final String? hintText; + final bool? widthNone; + final String? imgPath; + final String? Function(String? text)? validator; + final TextEditingController controller; + final bool isObscurable; + final Color? hintColor; + final Color? backgroundColor; + final Color? borderColor; + final Color? errorBorderColor; + final Color? errorTextColor; + final double? txtwidth; + final bool readOnly; + final bool? isdense; + final bool? isShowCursor; + final TextInputType? keyboardType; + final int? maxLength; + final double? txtheight; + final List? inputFormatters; + // void Function(dynamic value)? onChanged; + final ValueChanged? onChanged; + + @override + Widget build(BuildContext context) { + final isObscured = useState(true); + final boxDecoration = BoxDecoration( + borderRadius: BorderRadius.circular(10), + // boxShadow: [ + // BoxShadow( + // color: Colors.black26, // shadow color + // offset: Offset(0, 2), // x=0, y=4 → shadow goes downward + // blurRadius: 2, // soften the shadow + // spreadRadius: 0.2, // expand shadow + // ), + // ], + ); + final obscureBtn = IconButton( + onPressed: () => isObscured.value = !isObscured.value, + + icon: Icon( + isObscured.value + ? Icons.visibility_off_outlined + : Icons.visibility_outlined, + color: const Color(0xff9EA2A9), + ), + ); + final inputDecoration = InputDecoration( + isDense: isdense ?? false, + errorStyle: TextStyle(color: Color(0xFFD83731)), + errorBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(5), + borderSide: BorderSide( + color: errorBorderColor ?? Color(0xFFD83731), + width: 1, + ), + ), + focusedErrorBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(5), + borderSide: BorderSide( + color: errorBorderColor ?? Color(0xFFD83731), + width: 1, + ), + ), + filled: true, + fillColor: backgroundColor ?? Colors.white, + // contentPadding: const EdgeInsets.symmetric(vertical: 14, horizontal: 2), + contentPadding: const EdgeInsets.symmetric(vertical: 8, horizontal: 10), + + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(5), + // borderSide: BorderSide(color: Color(0xFF50A398)), + borderSide: BorderSide( + color: borderColor ?? Color(0xFFFFFFFF), + width: 0.1, + ), + ), + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(5), + // borderSide: BorderSide(color: Color(0xFF50A398)), + borderSide: BorderSide( + color: borderColor ?? backgroundColor ?? Color(0xFFFFFFFF), + width: 0.1, + ), + ), + focusedBorder: readOnly + ? OutlineInputBorder( + borderRadius: BorderRadius.circular(5), + // borderSide: BorderSide(color: Color(0xFF50A398)), + borderSide: BorderSide(color: borderColor ?? Color(0xFFFFFFFF)), + ) + : OutlineInputBorder( + borderRadius: BorderRadius.circular(5), + borderSide: BorderSide(color: Color(0xFF50A398), width: 2), + ), + hintText: hintText, + hintStyle: TextStyle( + // ✅ dynamic hint color + color: const Color(0xFFC3C6CB), + ), + // labelText: hintText, + // labelStyle: GoogleFonts.inter( + // fontSize: 10, + // // color: Colors.black, // customize here + // color: Colors.red, // customize here + // fontWeight: FontWeight.w400, + // ), + prefixIconConstraints: const BoxConstraints( + maxWidth: 25 + 16 + 10, + maxHeight: 25 + (8 * 2), + ), + prefixIcon: imgPath != null + ? Padding( + padding: const EdgeInsetsDirectional.only(start: 16, end: 10), + child: Image.asset( + imgPath!, + fit: BoxFit.fitHeight, + height: 25, + width: 25, + ), + ) + : null, + + suffixIcon: isObscurable ? obscureBtn : null, + ); + return Container( + height: txtheight ?? null, + child: DecoratedBox( + decoration: boxDecoration, + child: TextFormField( + controller: controller, + decoration: inputDecoration, + validator: validator, + obscureText: isObscurable && isObscured.value, + readOnly: readOnly, // ✅ now supported + keyboardType: keyboardType, // ✅ e.g. TextInputType.number + maxLength: maxLength, // ✅ max length + showCursor: isShowCursor ?? true, + enableInteractiveSelection: isShowCursor ?? true, + inputFormatters: + inputFormatters ?? + [FilteringTextInputFormatter.allow(RegExp(r'[a-z A-Z]'))], + // ✅ allow multiline if user sets maxLines / minLines + minLines: (keyboardType == TextInputType.multiline) ? 3 : 1, + maxLines: (keyboardType == TextInputType.multiline) ? null : 1, + style: GoogleFonts.inter( + fontSize: 12, // 👈 Change this to your desired size + fontWeight: FontWeight.w400, // optional + color: Colors.black, // optional + ), + ), + ), + ); + } +} + +class UpperCaseTextFormatter extends TextInputFormatter { + @override + TextEditingValue formatEditUpdate( + TextEditingValue oldValue, + TextEditingValue newValue, + ) { + return TextEditingValue( + text: newValue.text.toUpperCase(), + selection: newValue.selection, + ); + } +} diff --git a/lib/presentation/widgets/custom_Stdate_EnDate_Filter.dart b/lib/presentation/widgets/custom_Stdate_EnDate_Filter.dart index 2a61744..3a3af0f 100644 --- a/lib/presentation/widgets/custom_Stdate_EnDate_Filter.dart +++ b/lib/presentation/widgets/custom_Stdate_EnDate_Filter.dart @@ -144,7 +144,7 @@ class _DateFilterRowState extends ConsumerState { @override Widget build(BuildContext context) { - final spacing = 10.0; + final spacing = 5.0; // final rowChildren = [ // buildStartDate(context), @@ -186,7 +186,7 @@ class _DateFilterRowState extends ConsumerState { final buttons = [ Padding( - padding: const EdgeInsets.symmetric(vertical: 8.0), + padding: const EdgeInsets.symmetric(vertical: 0.0), // child: GestureDetector( // onTap: () { // if (formKey.currentState!.validate()) onFilter(); @@ -206,7 +206,7 @@ class _DateFilterRowState extends ConsumerState { ), ), Padding( - padding: const EdgeInsets.all(8.0), + padding: const EdgeInsets.all(0.0), // child: GestureDetector(onTap: onRefresh, child: Icon(Icons.refresh)), child: Tooltip( message: 'Refresh', @@ -235,11 +235,11 @@ class _DateFilterRowState extends ConsumerState { key: widget.formKey, child: widget.isMobile ? Column( - crossAxisAlignment: CrossAxisAlignment.end, + crossAxisAlignment: CrossAxisAlignment.start, children: getRowChildren(widget.isMobile, spacing), ) : Row( - crossAxisAlignment: CrossAxisAlignment.end, + crossAxisAlignment: CrossAxisAlignment.start, children: getRowChildren(widget.isMobile, spacing), ), ); @@ -249,8 +249,8 @@ class _DateFilterRowState extends ConsumerState { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text('Start Date', style: _textStyle), - SizedBox(height: 5), + // Text('Start Date', style: _textStyle), + // SizedBox(height: 5), ThemedDateField( hintText: "Select Date", txtwidth: ResponsiveLayout.isMobile(context) @@ -274,8 +274,8 @@ class _DateFilterRowState extends ConsumerState { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text('End Date', style: _textStyle), - SizedBox(height: 5), + // Text('End Date', style: _textStyle), + // SizedBox(height: 5), ThemedDateField( hintText: "Select Date", txtwidth: ResponsiveLayout.isMobile(context) @@ -336,8 +336,8 @@ class _DateFilterRowState extends ConsumerState { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text('Status', style: _textStyle), - SizedBox(height: 5), + // Text('Status', style: _textStyle), + // SizedBox(height: 5), SizedBox( height: 40, child: Container( @@ -362,6 +362,18 @@ class _DateFilterRowState extends ConsumerState { compareFn: (item, selectedItem) => item['id'] == selectedItem['id'], + dropdownBuilder: (context, selectedItem) => Align( + alignment: Alignment.centerLeft, + + child: Text( + selectedItem != null ? selectedItem['status'].toString() : "", + style: GoogleFonts.poppins(fontSize: 11, color: Colors.black), + overflow: TextOverflow.ellipsis, + maxLines: 1, + softWrap: false, + ), + ), + // validator: (val) { // if (val == null) { // return "Please select a status"; @@ -369,9 +381,19 @@ class _DateFilterRowState extends ConsumerState { // return null; // }, decoratorProps: DropDownDecoratorProps( - decoration: AppInputDecorations.dropdownDecoration( - label: "Select Status", - ).copyWith(filled: true, fillColor: Colors.white), + decoration: + AppInputDecorations.dropdownDecoration( + label: "Select Status", + ).copyWith( + filled: true, + fillColor: Colors.white, + isDense: true, // 👈 reduces built-in vertical padding + + contentPadding: const EdgeInsets.symmetric( + horizontal: 2, + vertical: 1, // 👈 adjust this to make the field shorter + ), + ), ), // popupProps: PopupProps.menu( // fit: FlexFit.loose, @@ -455,104 +477,124 @@ class _DateFilterRowState extends ConsumerState { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text("Staff Name", style: _textStyle), - SizedBox(height: 10), - Container( - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(5.0), - border: Border.all(color: Colors.grey.shade100), - // color: Colors.black, - ), - - width: ResponsiveLayout.isMobile(context) - ? null - : MediaQuery.of(context).size.width * 0.13, + // Text("Staff Name", style: _textStyle), + // SizedBox(height: 10), + SizedBox( height: 40, - child: DropdownSearch>( - // key: dropDownKey, - key: ValueKey(selectedStaff), - // selectedItem: selectedVehicle.isNotEmpty ? selectedVehicle : null, - selectedItem: selectedVehicle, - - items: (filter, infiniteScrollProps) { - return filteredStaffData; - }, - - itemAsString: (val) => val['name'].toString(), - compareFn: (item, selectedItem) => - item['id'] == selectedItem['id'], // ✅ compare by id - - decoratorProps: DropDownDecoratorProps( - decoration: - AppInputDecorations.dropdownDecoration( - label: "Select Staff ", - ).copyWith( - filled: true, - fillColor: - Colors.white, // 👈 makes the dropdown input white - ), + child: Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(5.0), + border: Border.all(color: Colors.grey.shade100), + // color: Colors.black, ), - popupProps: PopupProps.menu( - fit: FlexFit.loose, - constraints: BoxConstraints(maxHeight: 250), - menuProps: MenuProps( - backgroundColor: - Colors.white, // 👈 sets dropdown background to white - ), - showSearchBox: true, - searchFieldProps: TextFieldProps( - decoration: InputDecoration( - filled: true, - fillColor: Colors.white, - hintText: "Search Staff ...", - hintStyle: GoogleFonts.inter( - fontSize: 12, - color: Colors.black, - ), - enabledBorder: OutlineInputBorder( - borderSide: BorderSide( - color: Colors.white, - ), // 👈 Normal border - ), - focusedBorder: OutlineInputBorder( - borderSide: BorderSide( - color: Colors.white, - width: 1.5, - ), // 👈 Focused border - ), + width: ResponsiveLayout.isMobile(context) + ? null + : MediaQuery.of(context).size.width * 0.13, + // height: 45, + child: DropdownSearch>( + // key: dropDownKey, + key: ValueKey(selectedStaff), + // selectedItem: selectedVehicle.isNotEmpty ? selectedVehicle : null, + selectedItem: selectedVehicle, + + items: (filter, infiniteScrollProps) { + return filteredStaffData; + }, + + itemAsString: (val) => val['name'].toString(), + compareFn: (item, selectedItem) => + item['id'] == selectedItem['id'], // ✅ compare by id + dropdownBuilder: (context, selectedItem) => Align( + alignment: Alignment.centerLeft, + child: Text( + selectedItem != null ? selectedItem['name'].toString() : "", + style: GoogleFonts.poppins(fontSize: 11, color: Colors.black), + overflow: TextOverflow.ellipsis, + maxLines: 1, + softWrap: false, ), ), + decoratorProps: DropDownDecoratorProps( + decoration: + AppInputDecorations.dropdownDecoration( + label: "Select Staff ", + ).copyWith( + filled: true, + fillColor: + Colors.white, // 👈 makes the dropdown input white + contentPadding: const EdgeInsets.symmetric( + horizontal: 2, + vertical: 1, // 👈 adjust this to make the field shorter + ), + ), + ), - itemBuilder: (context, item, isDisabled, isSelected) { - return Container( - // color: isSelected ? Colors.blue.withOpacity(0.1) : null, - padding: const EdgeInsets.symmetric( - horizontal: 8, - vertical: 3, + popupProps: PopupProps.menu( + fit: FlexFit.loose, + constraints: BoxConstraints(maxHeight: 250), + menuProps: MenuProps( + backgroundColor: + Colors.white, // 👈 sets dropdown background to white + ), + showSearchBox: true, + searchFieldProps: TextFieldProps( + decoration: InputDecoration( + filled: true, + fillColor: Colors.white, + hintText: "Search Staff ...", + hintStyle: GoogleFonts.inter( + fontSize: 12, + color: Colors.black, + ), + enabledBorder: OutlineInputBorder( + borderSide: BorderSide( + color: Colors.white, + ), // 👈 Normal border + ), + focusedBorder: OutlineInputBorder( + borderSide: BorderSide( + color: Colors.white, + width: 1.5, + ), // 👈 Focused border + ), ), - child: Text( - item['name'].toString(), - style: GoogleFonts.inter(fontSize: 13, color: Colors.black), - ), - ); + ), + + itemBuilder: (context, item, isDisabled, isSelected) { + return Container( + // color: isSelected ? Colors.blue.withOpacity(0.1) : null, + padding: const EdgeInsets.symmetric( + horizontal: 8, + vertical: 3, + ), + child: Text( + item['name'].toString(), + style: GoogleFonts.inter( + fontSize: 13, + color: Colors.black, + ), + ), + ); + }, + + // constraints: BoxConstraints(), + ), + + onChanged: (val) { + if (val != null) { + print("Selected Staff : ${val['name']}"); + print("Id: ${val['id']}"); + selectedStaffName = val['name']; + selectedStaff = val['id']; + + if (widget.onFilterStaff != null) + widget.onFilterStaff!(val['id']); + // controllers['agentId']?.text = val['agent_code']; + // agentId = agent['id']; + } }, - // constraints: BoxConstraints(), ), - - onChanged: (val) { - if (val != null) { - print("Selected Staff : ${val['name']}"); - print("Id: ${val['id']}"); - selectedStaffName = val['name']; - selectedStaff = val['id']; - - if (widget.onFilterStaff != null) - widget.onFilterStaff!(val['id']); - // controllers['agentId']?.text = val['agent_code']; - // agentId = agent['id']; - } - }, ), ), ], diff --git a/lib/presentation/widgets/topbar.dart b/lib/presentation/widgets/topbar.dart index ae6660b..c2db9f2 100644 --- a/lib/presentation/widgets/topbar.dart +++ b/lib/presentation/widgets/topbar.dart @@ -65,6 +65,10 @@ class _TopBarState extends ConsumerState { titleSpacing: widget.isMobile ? 0 : 30, title: Row( children: [ + IconButton( + icon: const Icon(Icons.menu, color: Colors.black87), + onPressed: widget.onMenuPressed, + ), if (!widget.isMobile) Image.asset( "assets/login/nhance-partner-logo.png", @@ -103,11 +107,7 @@ class _TopBarState extends ConsumerState { Spacer(), - // else if (onMenuPressed != null) - // IconButton( - // icon: const Icon(Icons.menu, color: Colors.black87), - // onPressed: onMenuPressed, - // ), + // if (onMenuPressed != null) if (role == 'agent') ...[ GestureDetector( onTap: () async { diff --git a/pubspec.yaml b/pubspec.yaml index 289d8f5..ed70ebc 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -70,6 +70,8 @@ dependencies: file_picker: ^10.3.3 fluttertoast: ^9.0.0 + + dev_dependencies: flutter_test: sdk: flutter