diff --git a/.flutter-plugins-dependencies b/.flutter-plugins-dependencies index 89b1713..2dee264 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-26 11:58:11.463499","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-28 18:59:48.448938","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 b132a91..88c5e2a 100644 --- a/lib/core/routing/app_router.dart +++ b/lib/core/routing/app_router.dart @@ -27,6 +27,7 @@ import '../../presentation/screens/home/home_screen.dart'; import '../../presentation/screens/login/login_screen.dart'; import '../../presentation/screens/payout/payout_screen.dart'; import '../../presentation/screens/splash/splash_screen.dart'; +import '../../presentation/screens/staff/Enquiry/enquiry_inline_Edit_17Nov.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'; @@ -178,6 +179,8 @@ final GoRouter appRouter = GoRouter( GoRoute( path: AppRoutes.enquiryForStaff, builder: (context, state) => const EnquiryListStaffInline(), + // builder: (context, state) => const EnquiryListStaffInlineTST(), + // builder: (context, state) => const EnquiryStaff(), ), GoRoute( diff --git a/lib/core/services/api_service.dart b/lib/core/services/api_service.dart index 18d102b..9d3b57e 100644 --- a/lib/core/services/api_service.dart +++ b/lib/core/services/api_service.dart @@ -91,6 +91,44 @@ class ApiService { } // --------------------------- COMMON FILES __________________________________________ + Future> createUserData(data, path) async { + print('path - $path'); + final url = Uri.parse('${Env.apiUrl}$path'); + + if (_token == null) { + await _initializeToken(); + } + + print("data------- $data}"); + + final headers = { + 'Authorization': 'Bearer $_token' ?? '', + 'Content-Type': 'application/json', + 'app-signature': Env.App_Signature, + }; + + final response = await _makePostRequestJson(url, data, headers); + return response; + } + + Future> fetchUploadEnquiryFiles(dynamic enqId) async { + // print(_token); + if (_token == null) { + await _initializeToken(); + } + + final url = Uri.parse( + '${Env.apiUrl}enquiry/enquiryFiles?enquiry_id=$enqId', + ); + + final headers = { + 'Authorization': 'Bearer $_token' ?? '', + 'app-signature': Env.App_Signature, + }; + final response = await _makeGetRequest(url, headers); + return response; + } + Future> logoutUsingAPI(agentId) async { // print(_token); if (_token == null) { @@ -762,6 +800,23 @@ class ApiService { //------------------------------------ Quotation ------------------------------------------------ + Future> fetchQuickQuotationList(int managerId) async { + // print(_token); + if (_token == null) { + await _initializeToken(); + } + + final url = Uri.parse( + '${Env.apiUrl}enquiry/getQuickQuoteEnquiryList?manager_id=${managerId}', + ); + final headers = { + 'Authorization': 'Bearer $_token' ?? '', + 'app-signature': Env.App_Signature, + }; + final response = await _makeGetRequest(url, headers); + return response; + } + Future> fetchQuotationList(int managerId) async { // print(_token); if (_token == null) { @@ -1212,7 +1267,6 @@ class ApiService { // --------------------------------- PayOut Module---------------------------------------------- Future> getCommissionRateList(data) async { - final url = Uri.parse('${Env.apiUrl}/invoice/commission-rate-list'); // final token = await getToken(); // Fetch token @@ -1235,7 +1289,6 @@ class ApiService { } Future> getCreateOrUpdate(data) async { - final url = Uri.parse('${Env.apiUrl}/invoice/create-or-update'); // final token = await getToken(); // Fetch token @@ -1289,6 +1342,22 @@ class ApiService { return response; } + Future> delete(ID, path) async { + // print(_token); + if (_token == null) { + await _initializeToken(); + } + + final url = Uri.parse('${Env.apiUrl}$path'); + + final headers = { + 'Authorization': 'Bearer $_token' ?? '', + 'app-signature': Env.App_Signature, + }; + final response = await _makeGetRequest(url, headers); + return response; + } + Future> getInvoiceDetails(ID) async { // print(_token); if (_token == null) { diff --git a/lib/presentation/layouts/appheader.dart b/lib/presentation/layouts/appheader.dart index b3d2662..80ac54f 100644 --- a/lib/presentation/layouts/appheader.dart +++ b/lib/presentation/layouts/appheader.dart @@ -11,6 +11,8 @@ import '../../data/services/auth_service.dart'; import '../providers/manager_provider.dart'; import '../providers/userRoleProvider.dart'; import '../screens/UserManagement/Profile/profile_web.dart'; +import '../screens/staff/Enquiry/Proposal_QuickCreation/Proposal_QuickCreation.dart'; +import '../themes/indicators/side_Drawer_Panel.dart'; class AppHeader extends ConsumerStatefulWidget implements PreferredSizeWidget { const AppHeader({super.key}); @@ -380,7 +382,11 @@ class _AppHeaderState extends ConsumerState { size: 20, ), onPressed: () { - debugPrint("Notifications tapped"); + SideDrawerPanel.show( + context: context, + title: 'Proposal Creation', + child: CreateProposal_Quick(), + ); }, tooltip: 'Create Proposal', ), diff --git a/lib/presentation/screens/staff/Enquiry/Proposal_QuickCreation/Proposal_QuickCreation.dart b/lib/presentation/screens/staff/Enquiry/Proposal_QuickCreation/Proposal_QuickCreation.dart new file mode 100644 index 0000000..8093ec9 --- /dev/null +++ b/lib/presentation/screens/staff/Enquiry/Proposal_QuickCreation/Proposal_QuickCreation.dart @@ -0,0 +1,1542 @@ +import 'dart:async'; +import 'dart:convert'; + +import 'package:dropdown_search/dropdown_search.dart'; +import 'package:file_picker/file_picker.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:go_router/go_router.dart'; +import 'package:google_fonts/google_fonts.dart'; +import 'package:http/http.dart' as http; +import 'package:nhance_partner/data/utils/toastNotification.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 '../../../../../data/services/auth_service.dart'; +import '../../../../../data/utils/validators.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/input_field_decoration.dart'; +import '../../../../themes/indicators/text_field_theme.dart'; +import '../../../../themes/indicators/text_field_theme_inline_editor.dart' + hide UpperCaseTextFormatter; + +class CreateProposal_Quick extends ConsumerStatefulWidget { + // final dynamic userId; + // final dynamic managerId; + // final dynamic selectedEnquiryId; + // final dynamic selectedInsurdId; + // final dynamic selectedQuotationFrmListdata; + // final dynamic selectedQuotationFrmListId; + // final void Function(String value) onSubmit; + + const CreateProposal_Quick({ + super.key, + // required this.userId, + // required this.managerId, + // required this.selectedEnquiryId, + // required this.selectedInsurdId, + // this.selectedQuotationFrmListdata, + // this.selectedQuotationFrmListId, + // required this.onSubmit, + }); + + @override + ConsumerState createState() => + CreateProposal_QuickFormState(); +} + +class CreateProposal_QuickFormState + extends ConsumerState { + late ApiService apiService; + String? _token; + + String? selectedFileNames; + + List> getInsurersData = []; + List> filteredInsurersData = []; + String? selectedInsurer; + + bool isLoading = false; + bool _autoValidate = false; + late TextEditingController controller; + Map controllers = {}; + + final _formKey = GlobalKey(); + + String? docUploadedFileUrlFromApi; + String? selectedId; + PlatformFile? docUploadedFile; + + final GlobalKey>> dropDownKey = + GlobalKey>>(); + + final GlobalKey>> dropDownKeyBroker = + GlobalKey>>(); + final GlobalKey>> dropDownKeyAgent = + GlobalKey>>(); + + final GlobalKey>> + dropDownKeyInsurer = GlobalKey>>(); + + final GlobalKey>> + dropDownKeyInsurerEnqAsgn = + GlobalKey>>(); + + final GlobalKey>> + dropDownSelectPaymentModeKey = + GlobalKey>>(); + + List tabHeader = ['idv', 'premium_Amount', 'insurer', 'regNo']; + + List rows = []; + + List> getBrokerData = []; + List> filteredBrokerData = []; + String? selectedBroker; + + List> getPaymentModeData = []; + List> filteredPaymentModeData = []; + + List> getInsuranceTypeData = []; + List> filteredInsuranceData = []; + + List> getAgentListData = []; + List> filteredAgentData = []; + + List> getCreatedProposalData = []; + List> originalData = []; + List> filteredCreatedProposalData = []; + + String? selectedAgent; + String? selectedInsPlanType; + String? selectedEndorsement; + String? selectedPaymentMode; + + dynamic roleId; + dynamic userId; + dynamic managerId; + + List> quotationList = []; + + Map quotationData() { + final data = { + "insurer_id": selectedInsurer, + "premium_amount": controllers["premium_Amount"]?.text, + "insurance_plan_type_id": selectedInsPlanType, + }; + return data; + } + + Map dataDetails() { + print('checking agent id $selectedAgent'); + + final data = { + "agent_id": + ((roleId == 'handler') || + (roleId == 'manager') || + (roleId == 'staff')) + ? selectedAgent + : userId, + "name": controllers["name"]?.text, + "mobile": controllers["mobile"]?.text, + "email": controllers["email"]?.text, + "reg_no": controllers["regNo"]?.text, + + // "vehicle_type_id": selectedVehicleType, + "is_quick_quote": "1", + "quotation": quotationList, + // "is_data_created_by_handler": roleId == 'handler' ? '1' : '0', + // "is_data_created_by_manager": roleId == 'manager' ? '1' : '0', + "is_data_created_by_staff": roleId != null ? '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": (roleId == 'staff') ? userId : selectedStaff, + "insurer_id": selectedInsurer, + "broker_id": selectedBroker, + "manager_id": managerId, + "created_by": userId, + }; + + return data; + } + + @override + void initState() { + super.initState(); + apiService = ApiService(); + for (String field in tabHeader) { + controllers[field] = TextEditingController(); + } + + Future.microtask(() async { + roleId = ref.read(userRoleProvider); + userId = ref.read(userIdProvider); + managerId = ref.read(managerIdProvider); + + final prefs = await SharedPreferences.getInstance(); + + if (managerId != null) { + print('managerId - $managerId'); + getAgentList(managerId); + getQuickQuotationList(managerId); + } + }); + // 🔹 Init logic here (API calls, token fetch, etc.) + _initializeToken(); + getInsuranceType(); + getPaymentMode(); + getInsurers(); + updateData(); + getBroker(); + } + + Future _initializeToken() async { + _token = await AuthService.getToken(); + print("APISERTOKEN - $_token"); + } + + @override + void dispose() { + // Dispose all TextEditingControllers + for (var controller in controllers.values) { + controller.dispose(); + } + super.dispose(); + } + + void reset1() { + print('RESET'); + _formKey.currentState?.reset(); + // Clear all TextEditingControllers + for (var controller in controllers.values) { + controller.clear(); + } + dropDownKey.currentState?.changeSelectedItem(null); + dropDownKeyInsurer.currentState?.changeSelectedItem(null); + + // Reset dropdowns / selections + selectedInsurer = null; + selectedInsPlanType = null; + selectedEndorsement = null; + + // Reset file selection + selectedFileNames = null; + docUploadedFile = null; + docUploadedFileUrlFromApi = null; + + // Reset selected ID + selectedId = null; + + // Trigger UI update + setState(() {}); + } + + void reset() { + print('RESET'); + _formKey.currentState?.reset(); + // Clear all TextEditingControllers + for (var controller in controllers.values) { + controller.clear(); + } + dropDownKey.currentState?.changeSelectedItem(null); + dropDownKeyInsurer.currentState?.changeSelectedItem(null); + dropDownKeyAgent.currentState?.changeSelectedItem(null); + dropDownKeyInsurerEnqAsgn.currentState?.changeSelectedItem(null); + dropDownKeyBroker.currentState?.changeSelectedItem(null); + + // Reset dropdowns / selections + selectedInsurer = null; + selectedInsPlanType = null; + selectedEndorsement = null; + selectedAgent = null; + selectedBroker = null; + quotationList.clear(); + + // Reset file selection + selectedFileNames = null; + docUploadedFile = null; + docUploadedFileUrlFromApi = null; + + // Reset selected ID + selectedId = null; + + // Trigger UI update + setState(() {}); + } + + Future getQuickQuotationList(int managerId) async { + print('getClaimList called'); + setState(() { + isLoading = true; + }); + + try { + final response = await apiService.fetchQuickQuotationList(managerId); + + if (response['status'] == 'success') { + print('getAgentListData - ${response['data']}'); + setState(() { + getCreatedProposalData = List>.from( + response['data'], + ); + // print('API Data - $getClaimPolicies'); + // + originalData = getCreatedProposalData; + filteredCreatedProposalData = List.from(originalData); + print('filteredCreatedProposalData - $filteredCreatedProposalData'); + }); + } else { + // getClaimPolicies = []; + // originalData = getClaimPolicies; + // filteredData = List.from(originalData); + // print('originalData - $getClaimPolicies'); + // print('Request failed: ${response['code']}'); + } + } 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; + }); + } + } + + 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; + }); + } + } + + void updateData() {} + + 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 getInsuranceType() async { + print('getClaimList called'); + setState(() { + isLoading = true; + }); + + try { + final response = await apiService.fetchMasterDropDown('InsuranceType'); + + if (response['status'] == 200) { + print('getInsuranceTypeData - ${response['data']}'); + setState(() { + getInsuranceTypeData = List>.from( + response['data'], + ); + print('API Data - $getInsuranceTypeData'); + + filteredInsuranceData = List.from(getInsuranceTypeData); + print('originalData - $filteredInsuranceData'); + }); + } else { + getInsuranceTypeData = []; + filteredInsuranceData = []; + } + } catch (e) { + print('Exception occurred: $e'); + } finally { + setState(() { + isLoading = false; + }); + } + } + + Future getPaymentMode() async { + print('getPaymentMode called'); + setState(() { + isLoading = true; + }); + + try { + final response = await apiService.fetchMasterDropDown('PaymentMode'); + + if (response['status'] == 200) { + print('getPaymentModeData - ${response['data']}'); + setState(() { + getPaymentModeData = List>.from( + response['data'], + ); + print('API Data - $getPaymentModeData'); + + filteredPaymentModeData = List.from(getPaymentModeData); + print('originalData - $filteredPaymentModeData'); + }); + } else { + getPaymentModeData = []; + filteredPaymentModeData = []; + } + } catch (e) { + print('Exception occurred: $e'); + } finally { + setState(() { + isLoading = false; + }); + } + } + + void addQuotationRow() { + if (selectedInsurer != null && + selectedInsPlanType != null && + controllers["premium_Amount"]?.text.isNotEmpty == true) { + setState(() { + quotationList.add({ + "insurer_id": selectedInsurer, + "premium_amount": controllers["premium_Amount"]?.text, + "insurance_plan_type_id": selectedInsPlanType, + "insurer_name": getInsurerName(selectedInsurer), + "plan_type_name": getPlanTypeName(selectedInsPlanType), + }); + + // Clear fields after adding + selectedInsurer = null; + selectedInsPlanType = null; + controllers["premium_Amount"]?.clear(); + dropDownKeyInsurerEnqAsgn.currentState?.changeSelectedItem(null); + dropDownKey.currentState?.changeSelectedItem(null); + }); + } + } + + void removeQuotationRow(int index) { + setState(() { + quotationList.removeAt(index); + }); + } + + String getInsurerName(String? id) { + if (id == null) return ''; + final insurer = filteredInsurersData.firstWhere( + (item) => item['id'].toString() == id, + orElse: () => {}, + ); + return insurer['short_name']?.toString() ?? ''; + } + + String getPlanTypeName(String? id) { + if (id == null) return ''; + final plan = filteredInsuranceData.firstWhere( + (item) => item['id'].toString() == id, + orElse: () => {}, + ); + return plan['insurance_plan_type']?.toString() ?? ''; + } + + void handleDone() { + if (!_formKey.currentState!.validate()) return; + + // setState(() { + // _autoValidate = true; // enable autovalidation after first save attempt + // }); + setState(() { + if (_formKey.currentState!.validate()) { + dataDetails(); + final dataSet = dataDetails(); + print("dataSetAgent - $dataSet"); + // print("managerId - $managerId ,userId - $userId "); + createUserData(dataSet); + } else { + // isDi sable = false; + } + }); + } + + Future createUserData(Map userData) async { + // final bool isUpdating = widget.selectedQuotationFrmListId != null; + // final id = widget.selectedQuotationFrmListId!; + + print("userDatauserData1 - $userData"); + // final bool isUpdating = widget.selectedQuotationFrmListId != null; + // final id = widget.selectedQuotationFrmListId; // keep nullable + + // print('id - $id'); + final uri = Uri.parse( + // isUpdating + // ? '${Env.apiUrl}quotation/updateQuotation' + // : '${Env.apiUrl}quotation/createQuotation', + '${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; + + print("USerDAta - $userData"); + + userData.forEach((key, value) { + // if (key != 'certificate_file_name') { + request.fields[key] = value.toString(); + print("✅ Encoded $key: ${request.fields[key]}"); + // } + }); + + if (docUploadedFile != null) { + try { + if (docUploadedFile!.bytes != null) { + final multipartFile = http.MultipartFile.fromBytes( + 'policy_pdf_file_name', + docUploadedFile!.bytes!, + filename: docUploadedFile!.name, + ); + request.files.add(multipartFile); + } else if (docUploadedFile!.path != null) { + final multipartFile = await http.MultipartFile.fromPath( + 'policy_pdf_file_name', + docUploadedFile!.path!, + filename: docUploadedFile!.name, + ); + request.files.add(multipartFile); + } + print("📎 File attached: ${docUploadedFile!.name}"); + } catch (e) { + print("❌ Failed to attach file: $e"); + } + } + + 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("✅ Agent submitted successfully!"); + + print("Response: ${response.body}"); + // ToastHelper.showErrorToast(context, 'Proposal Created'); + reset(); + Navigator.of(context).pop(); + } else if (response.statusCode == 403) { + await apiService.clearLocalStorageAndRedirect(); + } else { + final responseBody = jsonDecode(response.body); + dynamic msg = responseBody['data']; + print("❌ Submission failed. Status: ${response.statusCode}"); + print("Body: ${response.body}"); + + ToastHelper.showErrorToast(context, 'Proposal Creation Failed'); + } + } catch (e) { + print("🔥 Error submitting user: $e"); + } + } + + @override + Widget build(BuildContext context) { + return Form( + key: _formKey, + child: Column( + children: [ + Container( + padding: EdgeInsets.all(8.0), + + decoration: BoxDecoration( + // color: Colors.amber, + gradient: const LinearGradient( + colors: [ + Color(0xFFE4F5F1), // very light teal + // Color(0xFF8CCABE), // very light teal + // Color(0xFFC4E7DA), // very light teal + Color(0xFFE8F7F4), + ], + begin: Alignment.topLeft, + end: Alignment.bottomRight, + ), + borderRadius: BorderRadius.circular(8.0), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + buildAgentName(context), + const SizedBox(width: 20), + buildVehicleNumber(context), + ], + ), + Divider(thickness: 0.3, color: Colors.blueGrey.shade100), + SizedBox(height: 2), + Row( + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + buildInsurer(context), + const SizedBox(width: 20), + buildInsurancePlanType(context), + const SizedBox(width: 20), + buildPremiumAmnt(context), + const SizedBox(width: 10), + Row( + children: [ + GestureDetector( + onTap: () { + addQuotationRow(); + }, + child: Container( + padding: const EdgeInsets.symmetric( + horizontal: 2, + vertical: 2, + ), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(20.0), + color: Colors.blue, + ), + child: Icon( + Icons.add, + color: Colors.white, + size: 14, + ), + ), + ), + ], + ), + ], + ), + + if (quotationList.isNotEmpty) ...[ + const SizedBox(height: 10), + + Text( + 'Added Proposal (${quotationList.length})', + style: GoogleFonts.poppins( + fontSize: 12, + color: Color(0xFF334155), + ), + ), + const SizedBox(height: 10), + GridView.builder( + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + itemCount: quotationList.length, + gridDelegate: + const SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: 3, // 👈 3 cards per row + mainAxisSpacing: 16, + crossAxisSpacing: 16, + childAspectRatio: + 3, // 👈 controls card shape (adjust if needed) + ), + itemBuilder: (context, index) { + final item = quotationList[index]; + + return Container( + padding: const EdgeInsets.all(12), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(8), + border: Border.all(color: const Color(0xFFE2E8F0)), + boxShadow: [ + BoxShadow( + color: Colors.black.withOpacity(0.05), + blurRadius: 1, + offset: const Offset(0, 1), + ), + ], + ), + child: Stack( + children: [ + /// 🟦 Card Content + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + item["insurer_name"], + style: GoogleFonts.poppins( + fontSize: 11, + fontWeight: FontWeight.w600, + color: Color(0xFF334155), + ), + ), + const SizedBox(height: 6), + + Row( + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + Text( + item["plan_type_name"], + style: GoogleFonts.poppins( + fontSize: 11, + fontWeight: FontWeight.w400, + color: Colors.black, + ), + ), + Text( + "₹${item["premium_amount"].toString().replaceAll("\$", "")}", + style: GoogleFonts.poppins( + fontSize: 11, + fontWeight: FontWeight.w400, + color: Colors.blueGrey, + ), + ), + ], + ), + ], + ), + + /// ❌ Delete Icon + Positioned( + top: 0, + right: 0, + child: GestureDetector( + onTap: () => removeQuotationRow(index), + child: Container( + padding: const EdgeInsets.all(2), + // decoration: BoxDecoration( + // color: Colors.red.shade50, + // borderRadius: BorderRadius.circular(4), + // ), + child: Icon( + Icons.close, + size: 16, + color: Colors.red.shade700, + ), + ), + ), + ), + ], + ), + ); + }, + ), + ], + + // ✅ END OF TABLE CODE + SizedBox(height: 20), + + Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + GestureDetector( + onTap: () { + handleDone(); + }, + child: Container( + padding: const EdgeInsets.symmetric( + horizontal: 5, + vertical: 5, + ), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(4.0), + color: const Color(0xFF2E7D6E), + ), + child: Text( + 'Save', + style: GoogleFonts.poppins( + color: Colors.white, + fontSize: 12, + fontWeight: FontWeight.w400, + ), + ), + ), + ), + ], + ), + ], + ), + ), + + SizedBox(height: 10), + Container( + padding: EdgeInsets.all(8.0), + width: double.infinity, + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(8.0), + boxShadow: [ + BoxShadow( + color: Colors.blueGrey.shade100, + spreadRadius: 0.2, + blurRadius: 1, + offset: const Offset(0, 1), // Horizontal, Vertical + ), + ], + ), + child: DataTable( + headingRowColor: WidgetStatePropertyAll(Color(0xFFEFF6FF)), + headingRowHeight: 30, + dividerThickness: 0.3, + columnSpacing: 10, + columns: [ + DataColumn(label: Text("Agent Name", style: _tableHeaderStyle)), + DataColumn(label: Text("Reg No", style: _tableHeaderStyle)), + DataColumn(label: Text("Insurer", style: _tableHeaderStyle)), + DataColumn(label: Text("Premium", style: _tableHeaderStyle)), + DataColumn(label: Text("Action", style: _tableHeaderStyle)), + ], + + rows: filteredCreatedProposalData.map((item) { + return DataRow( + cells: [ + DataCell( + Text(item["agent_name"] ?? "-", style: _tableDataStyle), + ), + DataCell( + Text(item["reg_no"] ?? "-", style: _tableDataStyle), + ), + DataCell( + Text( + item["insurer_short_name"] ?? "-", + style: _tableDataStyle, + ), + ), + DataCell( + Text( + item["premium_amount"]?.toString() ?? "-", + style: _tableDataStyle, + ), + ), + DataCell( + TextButton( + onPressed: () {}, + child: Text( + 'PROCEED', + style: GoogleFonts.poppins( + fontSize: 10, + fontWeight: FontWeight.w500, + ), + ), + ), + ), + ], + ); + }).toList(), + ), + ), + ], + ), + ); + } + + Widget buildIdv(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('IDV *', style: _textStyle), + SizedBox(height: 10), + ThemedFormField( + controller: controllers['idv']!, + + inputFormatters: [ + FilteringTextInputFormatter.allow(RegExp(r'[0-9.]')), + ], + validator: (value) => Validators.doubleNumber(value, "IDV"), + borderColor: Color(0xFFE2E8F0), + highlightColor: Color(0xFF50A398), + // validator: (value) => Validators.number(value, "IDV"), + // backgroundColor: Color(0xFFEDF6F5), + // readOnly: true, + txtwidth: ResponsiveLayout.isMobile(context) + ? null + : MediaQuery.of(context).size.width * 0.08, + // txtheight: 35, + ), + ], + ); + } + + Widget buildAgentName(BuildContext context, {fromHeader = true}) { + Map? selectedAgntName = filteredAgentData.firstWhere( + (item) => item['id'].toString() == selectedAgent, + orElse: () => {}, + ); + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('Partner *', style: _textStyle), + + SizedBox(height: 5), + SizedBox( + width: MediaQuery.of(context).size.width * 0.25, + // width: fromHeader + // ? MediaQuery.of(context).size.width * 0.11 + // : MediaQuery.of(context).size.width * 0.08, + height: 30, + child: DropdownSearch>( + key: dropDownKeyAgent, + selectedItem: selectedAgntName.isNotEmpty ? selectedAgntName : null, + // items: (filter, infiniteScrollProps) { + // return filteredAgentData; + // }, + items: (filter, infiniteScrollProps) async { + if (filter.isEmpty) { + return filteredAgentData; + } + return filteredAgentData.where((item) { + return item['name'].toString().toLowerCase().contains( + filter.toLowerCase(), + ); + }).toList(); + }, + + 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; + }, + suffixProps: DropdownSuffixProps( + // make sure the dropdown button is visible + dropdownButtonProps: DropdownButtonProps( + isVisible: true, + padding: EdgeInsets.zero, // remove default padding + constraints: const BoxConstraints( + // shrink icon tap area + minWidth: 12, + minHeight: 12, + ), + iconSize: 15, // smaller icon + // icon: const Icon(Icons.arrow_drop_down), + ), + ), + 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: "Partner Name", + ).copyWith( + errorStyle: GoogleFonts.poppins(fontSize: 0, height: 0.0), + filled: true, + fillColor: + Colors.white, // 👈 makes the dropdown input white + isDense: true, + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(5), + borderSide: const BorderSide( + color: Color(0xFFE2E8F0), + width: 0.5, + ), + ), + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(5), + borderSide: const BorderSide( + color: Color(0xFFE2E8F0), + width: 0.5, + ), + ), + contentPadding: EdgeInsets.symmetric( + horizontal: 8, + 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( + autofocus: true, + decoration: InputDecoration( + contentPadding: EdgeInsets.all(1), + filled: true, + fillColor: Colors.white, + hintText: "Partner Name", + hintStyle: GoogleFonts.inter( + fontSize: 10, + color: Colors.grey, + ), + 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 Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('Vehicle Number *', style: _textStyle), + + SizedBox(height: 5), + SizedBox( + width: MediaQuery.of(context).size.width * 0.2, + height: 30, + child: ThemedFormInlineField( + controller: controllers['regNo']!, + hintText: 'Vehicle Number', + borderColor: Color(0xFFE2E8F0), + highlightColor: Color(0xFF50A398), + padVertical: 10, + isdense: true, + + inputFormatters: [ + UpperCaseTextFormatter(), // 👈 custom formatter for uppercase + FilteringTextInputFormatter.allow(RegExp(r'[A-Za-z0-9- ]')), + ], + validator: (value) => Validators.requiredField(value, "regNo"), + // validator: (value) => Validators.requiredVechileNum(value, "regNo"), + widthNone: true, + ), + ), + ], + ); + } + + Widget buildInsurer(BuildContext context, {fromHeader = true}) { + Map? selectedInsurerd = filteredInsurersData.firstWhere( + (item) => item['id'].toString() == selectedInsurer, + orElse: () => {}, + ); + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('Insurer *', style: _textStyle), + + SizedBox(height: 5), + Container( + height: 30, + width: MediaQuery.of(context).size.width * 0.12, + child: DropdownSearch>( + key: dropDownKeyInsurerEnqAsgn, + selectedItem: selectedInsurerd.isNotEmpty ? selectedInsurerd : null, + items: (filter, infiniteScrollProps) async { + if (filter.isEmpty) { + return filteredInsurersData; + } + return filteredInsurersData.where((item) { + return item['short_name'].toString().toLowerCase().contains( + filter.toLowerCase(), + ); + }).toList(); + }, + itemAsString: (val) => val['short_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; + }, + suffixProps: DropdownSuffixProps( + // make sure the dropdown button is visible + dropdownButtonProps: DropdownButtonProps( + isVisible: true, + padding: EdgeInsets.zero, // remove default padding + constraints: const BoxConstraints( + // shrink icon tap area + minWidth: 12, + minHeight: 12, + ), + iconSize: 15, // smaller icon + // icon: const Icon(Icons.arrow_drop_down), + ), + ), + + dropdownBuilder: (context, selectedItem) { + return Container( + padding: EdgeInsets.symmetric(vertical: 0, horizontal: 4), + alignment: Alignment.centerLeft, + height: 22, // 👈 FORCE HEIGHT + child: Text( + selectedItem?['short_name'] ?? '', + style: GoogleFonts.poppins(fontSize: 11), + overflow: TextOverflow.ellipsis, + ), + ); + }, + + decoratorProps: DropDownDecoratorProps( + decoration: + AppInputDecorations.dropdownDecoration( + label: "Insurer", + ).copyWith( + errorStyle: GoogleFonts.poppins(fontSize: 0, height: 0.0), + filled: true, + fillColor: + Colors.white, // 👈 makes the dropdown input white + isDense: true, + // contentPadding: EdgeInsets.zero, + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(5), + borderSide: const BorderSide( + color: Color(0xFFE2E8F0), + width: 0.5, + ), + ), + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(5), + borderSide: const BorderSide( + color: Color(0xFFE2E8F0), + width: 0.5, + ), + ), + contentPadding: EdgeInsets.symmetric( + horizontal: 4, + vertical: 1, + ), + ), + ), + popupProps: PopupProps.menu( + fit: FlexFit.loose, + searchDelay: Duration(milliseconds: 200), + constraints: BoxConstraints(maxHeight: 250), + menuProps: MenuProps( + backgroundColor: + Colors.white, // 👈 sets dropdown background to white + ), + showSearchBox: true, + searchFieldProps: TextFieldProps( + autofocus: true, + decoration: InputDecoration( + contentPadding: EdgeInsets.all(1), + filled: true, + fillColor: Colors.white, + hintText: "Search Insurer...", + hintStyle: GoogleFonts.inter( + fontSize: 11, + 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: 1, + ), + child: Text( + item['short_name'].toString(), + style: GoogleFonts.inter(fontSize: 12, color: Colors.black), + ), + ); + }, + // constraints: BoxConstraints(), + ), + + onChanged: (val) { + if (val != null) { + print("Selected Insurer : ${val['short_name']}"); + print("Id: ${val['id']}"); + selectedInsurer = val['id']; + // controllers['agentId']?.text = val['agent_code']; + // agentId = agent['id']; + } + }, + ), + ), + ], + ); + } + + Widget buildPremiumAmnt(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('Premium Amount *', style: _textStyle), + SizedBox(height: 5), + SizedBox( + height: 30, + child: ThemedFormField( + controller: controllers['premium_Amount']!, + borderColor: Color(0xFFE2E8F0), + highlightColor: Color(0xFF50A398), + bordedRad: 5, + // backgroundColor: Color(0xFFEDF6F5), + inputFormatters: [ + FilteringTextInputFormatter.allow(RegExp(r'[0-9.]')), + ], + validator: (value) => + Validators.doubleNumber(value, "PremiumAmount"), + // validator: (value) => Validators.number(value, "PremiumAmount "), + txtwidth: ResponsiveLayout.isMobile(context) + ? null + : MediaQuery.of(context).size.width * 0.12, + // txtheight: 35, + ), + ), + ], + ); + } + + Widget buildInsurancePlanType(context) { + Map? selectedVehicle = filteredInsuranceData.firstWhere( + (item) => item['id'].toString() == selectedInsPlanType, + orElse: () => {}, + ); + + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text("Plan Type *", style: _textStyle), + SizedBox(height: 5), + SizedBox( + // color: Colors.white, + width: ResponsiveLayout.isMobile(context) + ? null + : MediaQuery.of(context).size.width * 0.12, + height: 30, + child: DropdownSearch>( + key: dropDownKey, + selectedItem: selectedVehicle.isNotEmpty ? selectedVehicle : null, + // items: (filter, infiniteScrollProps) { + // return filteredInsuranceData; + // }, + items: (filter, infiniteScrollProps) async { + if (filter.isEmpty) { + return filteredInsuranceData; + } + return filteredInsuranceData.where((item) { + return item['name'].toString().toLowerCase().contains( + filter.toLowerCase(), + ); + }).toList(); + }, + itemAsString: (val) => val['insurance_plan_type'].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( + errorStyle: GoogleFonts.poppins(fontSize: 0, height: 0.0), + filled: true, + fillColor: + Colors.white, // 👈 makes the dropdown input white + isDense: true, + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(5), + borderSide: const BorderSide( + color: Color(0xFFE2E8F0), + width: 0.5, + ), + ), + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(5), + borderSide: const BorderSide( + color: Color(0xFFE2E8F0), + width: 0.5, + ), + ), + contentPadding: EdgeInsets.symmetric( + horizontal: 8, + 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( + autofocus: true, + decoration: InputDecoration( + contentPadding: EdgeInsets.all(1), + filled: true, + fillColor: Colors.white, + hintText: "Search Plan 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 + ), + ), + ), + + 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['insurance_plan_type'].toString(), + style: GoogleFonts.inter(fontSize: 12, color: Colors.black), + ), + ); + }, + // searchFieldProps: TextFieldProps( + // decoration: InputDecoration( + // filled: true, + // fillColor: Colors.white, + // hintText: "Search Plan Type...", + // enabledBorder: OutlineInputBorder( + // borderSide: BorderSide( + // color: Colors.white, + // ), // 👈 Normal border + // ), + // focusedBorder: OutlineInputBorder( + // borderSide: BorderSide( + // color: Colors.white, + // width: 1.5, + // ), // 👈 Focused border + // ), + // ), + // ), + // constraints: BoxConstraints(), + ), + + onChanged: (val) { + if (val != null) { + print("Selected ClaimsType : ${val['insurance_plan_type']}"); + print("Id: ${val['id']}"); + selectedInsPlanType = val['id']; + // controllers['agentId']?.text = val['agent_code']; + // agentId = agent['id']; + } + }, + ), + ), + ], + ); + } + + Widget _rowLabelValue(String label, String value) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + value, + style: GoogleFonts.poppins( + fontSize: 13, + fontWeight: FontWeight.w600, + color: Colors.black, + ), + ), + ], + ); + } + + // ------------------- STyle --------------------------------- + + static final TextStyle _textStyle = GoogleFonts.poppins( + fontSize: 11, + color: Color(0XFF334155), + fontWeight: FontWeight.w500, + ); + + static final TextStyle _tableHeaderStyle = GoogleFonts.poppins( + fontSize: 11.5, + color: Color(0XFF475569), + fontWeight: FontWeight.w500, + ); + + static final TextStyle _tableDataStyle = GoogleFonts.poppins( + fontSize: 11.5, + color: Color(0XFF334155), + fontWeight: FontWeight.w400, + ); +} + +class QuotationRow { + TextEditingController premiumCtrl = TextEditingController(); + String? insurerId; + String? planTypeId; +} diff --git a/lib/presentation/screens/staff/Enquiry/Proposal_QuickCreation/re b/lib/presentation/screens/staff/Enquiry/Proposal_QuickCreation/re new file mode 100644 index 0000000..ca6fd80 --- /dev/null +++ b/lib/presentation/screens/staff/Enquiry/Proposal_QuickCreation/re @@ -0,0 +1,1469 @@ +import 'dart:convert'; + +import 'package:dropdown_search/dropdown_search.dart'; +import 'package:file_picker/file_picker.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:go_router/go_router.dart'; +import 'package:google_fonts/google_fonts.dart'; +import 'package:http/http.dart' as http; +import 'package:nhance_partner/data/utils/toastNotification.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 '../../../../../data/services/auth_service.dart'; +import '../../../../../data/utils/validators.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/input_field_decoration.dart'; +import '../../../../themes/indicators/text_field_theme.dart'; +import '../../../../themes/indicators/text_field_theme_inline_editor.dart' + hide UpperCaseTextFormatter; + +class CreateProposal_Quick extends ConsumerStatefulWidget { + // final dynamic userId; + // final dynamic managerId; + // final dynamic selectedEnquiryId; + // final dynamic selectedInsurdId; + // final dynamic selectedQuotationFrmListdata; + // final dynamic selectedQuotationFrmListId; + // final void Function(String value) onSubmit; + + const CreateProposal_Quick({ + super.key, + // required this.userId, + // required this.managerId, + // required this.selectedEnquiryId, + // required this.selectedInsurdId, + // this.selectedQuotationFrmListdata, + // this.selectedQuotationFrmListId, + // required this.onSubmit, + }); + + @override + ConsumerState createState() => + CreateProposal_QuickFormState(); +} + +class CreateProposal_QuickFormState + extends ConsumerState { + late ApiService apiService; + String? _token; + + String? selectedFileNames; + + List> getInsurersData = []; + List> filteredInsurersData = []; + String? selectedInsurer; + + bool isLoading = false; + bool _autoValidate = false; + late TextEditingController controller; + Map controllers = {}; + + final _formKey = GlobalKey(); + + String? docUploadedFileUrlFromApi; + String? selectedId; + PlatformFile? docUploadedFile; + + final GlobalKey>> dropDownKey = + GlobalKey>>(); + + final GlobalKey>> dropDownKeyBroker = + GlobalKey>>(); + final GlobalKey>> dropDownKeyAgent = + GlobalKey>>(); + + final GlobalKey>> + dropDownKeyInsurer = GlobalKey>>(); + + final GlobalKey>> + dropDownKeyInsurerEnqAsgn = + GlobalKey>>(); + + final GlobalKey>> + dropDownSelectPaymentModeKey = + GlobalKey>>(); + + List tabHeader = ['idv', 'premium_Amount', 'insurer', 'regNo']; + + List> getBrokerData = []; + List> filteredBrokerData = []; + String? selectedBroker; + + List> getPaymentModeData = []; + List> filteredPaymentModeData = []; + + List> getInsuranceTypeData = []; + List> filteredInsuranceData = []; + + List> getAgentListData = []; + List> filteredAgentData = []; + + String? selectedAgent; + String? selectedInsPlanType; + String? selectedEndorsement; + String? selectedPaymentMode; + + dynamic roleId; + dynamic userId; + dynamic managerId; + + Map quotationData() { + final data = { + // "enquiry_id": widget.selectedEnquiryId, + // "insured_declared_value": controllers["idv"]?.text, + "insurer_id": selectedInsurer, + "premium_amount": controllers["premium_Amount"]?.text, + "insurance_plan_type_id": selectedInsPlanType, + // "payment_mode_id": selectedPaymentMode, + // "broker_id": selectedBroker, + + // "additional_uploaded_file_name": "extra_doc.pdf", + // "created_by": widget.userId, + // "manager_id": widget.managerId, + }; + return data; + } + + Map dataDetails() { + print('checking agent id $selectedAgent'); + + final data = { + "agent_id": + ((roleId == 'handler') || + (roleId == 'manager') || + (roleId == 'staff')) + ? selectedAgent + : userId, + // "name": controllers["name"]?.text, + // "mobile": controllers["mobile"]?.text, + // "email": controllers["email"]?.text, + "reg_no": controllers["regNo"]?.text, + "is_data_created_by_staff": roleId != null ? '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": (roleId == 'staff') ? userId : selectedStaff, + "is_quick_quote": "1", + "quotation": [quotationData], + "insurer_id": selectedInsurer, + "broker_id": selectedBroker, + "manager_id": managerId, + "created_by": userId, + }; + + return data; + } + + @override + void initState() { + super.initState(); + apiService = ApiService(); + for (String field in tabHeader) { + controllers[field] = TextEditingController(); + } + Future.microtask(() async { + roleId = ref.read(userRoleProvider); + userId = ref.read(userIdProvider); + managerId = ref.read(managerIdProvider); + + final prefs = await SharedPreferences.getInstance(); + + if (managerId != null) { + print('managerId - $managerId'); + getAgentList(managerId); + } + }); + // 🔹 Init logic here (API calls, token fetch, etc.) + _initializeToken(); + getInsuranceType(); + getPaymentMode(); + getInsurers(); + updateData(); + getBroker(); + } + + Future _initializeToken() async { + _token = await AuthService.getToken(); + print("APISERTOKEN - $_token"); + } + + @override + void dispose() { + // Dispose all TextEditingControllers + for (var controller in controllers.values) { + controller.dispose(); + } + super.dispose(); + } + + void reset() { + print('RESET'); + _formKey.currentState?.reset(); + // Clear all TextEditingControllers + for (var controller in controllers.values) { + controller.clear(); + } + dropDownKey.currentState?.changeSelectedItem(null); + dropDownKeyInsurer.currentState?.changeSelectedItem(null); + + // Reset dropdowns / selections + selectedInsurer = null; + selectedInsPlanType = null; + selectedEndorsement = null; + + // Reset file selection + selectedFileNames = null; + docUploadedFile = null; + docUploadedFileUrlFromApi = null; + + // Reset selected ID + selectedId = null; + + // Trigger UI update + setState(() {}); + } + + 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; + }); + } + } + + 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; + }); + } + } + + void updateData() {} + + 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 getInsuranceType() async { + print('getClaimList called'); + setState(() { + isLoading = true; + }); + + try { + final response = await apiService.fetchMasterDropDown('InsuranceType'); + + if (response['status'] == 200) { + print('getInsuranceTypeData - ${response['data']}'); + setState(() { + getInsuranceTypeData = List>.from( + response['data'], + ); + print('API Data - $getInsuranceTypeData'); + + filteredInsuranceData = List.from(getInsuranceTypeData); + print('originalData - $filteredInsuranceData'); + }); + } else { + getInsuranceTypeData = []; + filteredInsuranceData = []; + } + } catch (e) { + print('Exception occurred: $e'); + } finally { + setState(() { + isLoading = false; + }); + } + } + + Future getPaymentMode() async { + print('getPaymentMode called'); + setState(() { + isLoading = true; + }); + + try { + final response = await apiService.fetchMasterDropDown('PaymentMode'); + + if (response['status'] == 200) { + print('getPaymentModeData - ${response['data']}'); + setState(() { + getPaymentModeData = List>.from( + response['data'], + ); + print('API Data - $getPaymentModeData'); + + filteredPaymentModeData = List.from(getPaymentModeData); + print('originalData - $filteredPaymentModeData'); + }); + } else { + getPaymentModeData = []; + filteredPaymentModeData = []; + } + } catch (e) { + print('Exception occurred: $e'); + } finally { + setState(() { + isLoading = false; + }); + } + } + + void handleDone() { + if (!_formKey.currentState!.validate()) return; + + // setState(() { + // _autoValidate = true; // enable autovalidation after first save attempt + // }); + setState(() { + if (_formKey.currentState!.validate()) { + dataDetails(); + final dataSet = dataDetails(); + print("dataSetAgent - $dataSet"); + // print("managerId - $managerId ,userId - $userId "); + createUserData(dataSet); + } else { + // isDi sable = false; + } + }); + } + + Future createUserData(Map userData) async { + // final bool isUpdating = widget.selectedQuotationFrmListId != null; + // final id = widget.selectedQuotationFrmListId!; + + print("userDatauserData1 - $userData"); + // final bool isUpdating = widget.selectedQuotationFrmListId != null; + // final id = widget.selectedQuotationFrmListId; // keep nullable + + // print('id - $id'); + final uri = Uri.parse( + // isUpdating + // ? '${Env.apiUrl}quotation/updateQuotation' + // : '${Env.apiUrl}quotation/createQuotation', + '${Env.apiUrl}quotation/proceedQuotation', + ); + 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) { + // print('Updatrinf'); + // // request.fields['_method'] = 'PUT'; + // request.fields['id'] = id!; + // request.fields['updated_by'] = widget.userId!.toString(); + // request.fields['created_by'] = widget.userId!.toString(); + // } else { + // print('Not Updatrinf'); + // request.fields['created_by'] = widget.userId!.toString(); + // } + + print("USerDAta - $userData"); + + // userData.forEach((key, value) { + // request.fields[key] = value.toString(); + // print("✅ Encoded travel_details2: ${request.fields[key]}"); + // }); + + userData.forEach((key, value) { + // if (key != 'certificate_file_name') { + request.fields[key] = value.toString(); + print("✅ Encoded $key: ${request.fields[key]}"); + // } + }); + + if (docUploadedFile != null) { + try { + if (docUploadedFile!.bytes != null) { + final multipartFile = http.MultipartFile.fromBytes( + 'policy_pdf_file_name', + docUploadedFile!.bytes!, + filename: docUploadedFile!.name, + ); + request.files.add(multipartFile); + } else if (docUploadedFile!.path != null) { + final multipartFile = await http.MultipartFile.fromPath( + 'policy_pdf_file_name', + docUploadedFile!.path!, + filename: docUploadedFile!.name, + ); + request.files.add(multipartFile); + } + print("📎 File attached: ${docUploadedFile!.name}"); + } catch (e) { + print("❌ Failed to attach file: $e"); + } + } + + 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("✅ Agent submitted successfully!"); + + print("Response: ${response.body}"); + // ToastHelper.showErrorToast(context, 'Proposal Created'); + reset(); + Navigator.of(context).pop(); + } else if (response.statusCode == 403) { + await apiService.clearLocalStorageAndRedirect(); + } else { + final responseBody = jsonDecode(response.body); + dynamic msg = responseBody['data']; + print("❌ Submission failed. Status: ${response.statusCode}"); + print("Body: ${response.body}"); + + ToastHelper.showErrorToast(context, 'Proposal Creation Failed'); + } + } catch (e) { + print("🔥 Error submitting user: $e"); + } + } + + @override + Widget build(BuildContext context) { + return Form( + key: _formKey, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + buildAgentName(context), + const SizedBox(width: 20), + buildVehicleNumber(context), + ], + ), + + SizedBox(height: 5), + Row( + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + buildInsurer(context), + const SizedBox(width: 20), + buildInsurancePlanType(context), + const SizedBox(width: 20), + buildPremiumAmnt(context), + const SizedBox(width: 10), + Row( + // mainAxisAlignment: MainAxisAlignment.end, + children: [ + GestureDetector( + onTap: () { + // handleDone(); + }, + child: Container( + padding: const EdgeInsets.symmetric( + horizontal: 2, + vertical: 2, + ), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(20.0), + color: Colors.blue, + ), + child: Icon(Icons.add, color: Colors.white, size: 14), + ), + ), + ], + ), + ], + ), + SizedBox(height: 20), + Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + GestureDetector( + onTap: () { + handleDone(); + // widget.onSubmit(controller.text.trim()); + // Navigator.of(context).pop(); + }, + child: Container( + padding: const EdgeInsets.symmetric( + horizontal: 5, + vertical: 5, + ), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(4.0), + color: const Color(0xFF2E7D6E), + ), + child: Text( + 'Save', + style: GoogleFonts.poppins( + color: Colors.white, + fontSize: 12, + fontWeight: FontWeight.w400, + ), + ), + ), + ), + ], + ), + ], + ), + ); + } + + // ------------------------- Claims Part ----------------------------------- + + Widget buildIdv(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('IDV *', style: _textStyle), + SizedBox(height: 10), + ThemedFormField( + controller: controllers['idv']!, + + inputFormatters: [ + FilteringTextInputFormatter.allow(RegExp(r'[0-9.]')), + ], + validator: (value) => Validators.doubleNumber(value, "IDV"), + borderColor: Color(0xFFE2E8F0), + highlightColor: Color(0xFF50A398), + // validator: (value) => Validators.number(value, "IDV"), + // backgroundColor: Color(0xFFEDF6F5), + // readOnly: true, + txtwidth: ResponsiveLayout.isMobile(context) + ? null + : MediaQuery.of(context).size.width * 0.08, + // txtheight: 35, + ), + ], + ); + } + + Widget buildAgentName(BuildContext context, {fromHeader = true}) { + Map? selectedAgntName = filteredAgentData.firstWhere( + (item) => item['id'].toString() == selectedAgent, + orElse: () => {}, + ); + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('Partner *', style: _textStyle), + + SizedBox(height: 5), + SizedBox( + width: MediaQuery.of(context).size.width * 0.1, + // width: fromHeader + // ? MediaQuery.of(context).size.width * 0.11 + // : MediaQuery.of(context).size.width * 0.08, + height: 30, + child: DropdownSearch>( + key: dropDownKeyAgent, + selectedItem: selectedAgntName.isNotEmpty ? selectedAgntName : null, + // items: (filter, infiniteScrollProps) { + // return filteredAgentData; + // }, + items: (filter, infiniteScrollProps) async { + if (filter.isEmpty) { + return filteredAgentData; + } + return filteredAgentData.where((item) { + return item['name'].toString().toLowerCase().contains( + filter.toLowerCase(), + ); + }).toList(); + }, + + 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; + }, + suffixProps: DropdownSuffixProps( + // make sure the dropdown button is visible + dropdownButtonProps: DropdownButtonProps( + isVisible: true, + padding: EdgeInsets.zero, // remove default padding + constraints: const BoxConstraints( + // shrink icon tap area + minWidth: 12, + minHeight: 12, + ), + iconSize: 15, // smaller icon + // icon: const Icon(Icons.arrow_drop_down), + ), + ), + 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: "Partner Name", + ).copyWith( + errorStyle: const TextStyle(fontSize: 0, height: 0.0), + filled: true, + fillColor: + Colors.white, // 👈 makes the dropdown input white + isDense: true, + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(5), + borderSide: const BorderSide( + color: Color(0xFFE2E8F0), + width: 0.5, + ), + ), + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(5), + borderSide: const BorderSide( + color: Color(0xFFE2E8F0), + width: 0.5, + ), + ), + contentPadding: EdgeInsets.symmetric( + horizontal: 8, + 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( + autofocus: true, + decoration: InputDecoration( + contentPadding: EdgeInsets.all(1), + filled: true, + fillColor: Colors.white, + hintText: "Partner Name", + hintStyle: GoogleFonts.inter( + fontSize: 10, + color: Colors.grey, + ), + 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 Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('Vehicle Number *', style: _textStyle), + + SizedBox(height: 5), + SizedBox( + width: MediaQuery.of(context).size.width * 0.1, + height: 30, + child: ThemedFormInlineField( + controller: controllers['regNo']!, + hintText: 'Vehicle Number', + borderColor: Color(0xFFE2E8F0), + highlightColor: Color(0xFF50A398), + padVertical: 10, + isdense: true, + + inputFormatters: [ + UpperCaseTextFormatter(), // 👈 custom formatter for uppercase + FilteringTextInputFormatter.allow(RegExp(r'[A-Za-z0-9- ]')), + ], + validator: (value) => Validators.requiredField(value, "regNo"), + // validator: (value) => Validators.requiredVechileNum(value, "regNo"), + widthNone: true, + ), + ), + ], + ); + } + + Widget buildInsurer(BuildContext context, {fromHeader = true}) { + Map? selectedInsurerd = filteredInsurersData.firstWhere( + (item) => item['id'].toString() == selectedInsurer, + orElse: () => {}, + ); + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('Insurer *', style: _textStyle), + + SizedBox(height: 5), + Container( + height: 30, + width: MediaQuery.of(context).size.width * 0.1, + child: DropdownSearch>( + key: dropDownKeyInsurerEnqAsgn, + selectedItem: selectedInsurerd.isNotEmpty ? selectedInsurerd : null, + items: (filter, infiniteScrollProps) async { + if (filter.isEmpty) { + return filteredInsurersData; + } + return filteredInsurersData.where((item) { + return item['name'].toString().toLowerCase().contains( + filter.toLowerCase(), + ); + }).toList(); + }, + 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; + }, + suffixProps: DropdownSuffixProps( + // make sure the dropdown button is visible + dropdownButtonProps: DropdownButtonProps( + isVisible: true, + padding: EdgeInsets.zero, // remove default padding + constraints: const BoxConstraints( + // shrink icon tap area + minWidth: 12, + minHeight: 12, + ), + iconSize: 15, // smaller icon + // icon: const Icon(Icons.arrow_drop_down), + ), + ), + + dropdownBuilder: (context, selectedItem) { + return Container( + padding: EdgeInsets.symmetric(vertical: 0, horizontal: 4), + alignment: Alignment.centerLeft, + height: 22, // 👈 FORCE HEIGHT + child: Text( + selectedItem?['name'] ?? '', + style: GoogleFonts.poppins(fontSize: 11), + overflow: TextOverflow.ellipsis, + ), + ); + }, + + decoratorProps: DropDownDecoratorProps( + decoration: + AppInputDecorations.dropdownDecoration( + label: "Insurer", + ).copyWith( + errorStyle: const TextStyle(fontSize: 0, height: 0.0), + filled: true, + fillColor: + Colors.white, // 👈 makes the dropdown input white + isDense: true, + // contentPadding: EdgeInsets.zero, + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(5), + borderSide: const BorderSide( + color: Color(0xFFE2E8F0), + width: 0.5, + ), + ), + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(5), + borderSide: const BorderSide( + color: Color(0xFFE2E8F0), + width: 0.5, + ), + ), + contentPadding: EdgeInsets.symmetric( + horizontal: 4, + vertical: 1, + ), + ), + ), + popupProps: PopupProps.menu( + fit: FlexFit.loose, + searchDelay: Duration(milliseconds: 200), + constraints: BoxConstraints(maxHeight: 250), + menuProps: MenuProps( + backgroundColor: + Colors.white, // 👈 sets dropdown background to white + ), + showSearchBox: true, + searchFieldProps: TextFieldProps( + autofocus: true, + decoration: InputDecoration( + contentPadding: EdgeInsets.all(1), + filled: true, + fillColor: Colors.white, + hintText: "Search Insurer...", + hintStyle: GoogleFonts.inter( + fontSize: 11, + 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: 1, + ), + 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 buildPremiumAmnt(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('Premium Amount *', style: _textStyle), + SizedBox(height: 5), + SizedBox( + height: 30, + child: ThemedFormField( + controller: controllers['premium_Amount']!, + borderColor: Color(0xFFE2E8F0), + highlightColor: Color(0xFF50A398), + // backgroundColor: Color(0xFFEDF6F5), + inputFormatters: [ + FilteringTextInputFormatter.allow(RegExp(r'[0-9.]')), + ], + validator: (value) => + Validators.doubleNumber(value, "PremiumAmount"), + // validator: (value) => Validators.number(value, "PremiumAmount "), + txtwidth: ResponsiveLayout.isMobile(context) + ? null + : MediaQuery.of(context).size.width * 0.08, + // txtheight: 35, + ), + ), + ], + ); + } + + Widget buildInsurancePlanType(context) { + Map? selectedVehicle = filteredInsuranceData.firstWhere( + (item) => item['id'].toString() == selectedInsPlanType, + orElse: () => {}, + ); + + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text("Plan Type *", style: _textStyle), + SizedBox(height: 5), + SizedBox( + // color: Colors.white, + width: ResponsiveLayout.isMobile(context) + ? null + : MediaQuery.of(context).size.width * 0.1, + height: 30, + child: DropdownSearch>( + key: dropDownKey, + selectedItem: selectedVehicle.isNotEmpty ? selectedVehicle : null, + // items: (filter, infiniteScrollProps) { + // return filteredInsuranceData; + // }, + items: (filter, infiniteScrollProps) async { + if (filter.isEmpty) { + return filteredInsuranceData; + } + return filteredInsuranceData.where((item) { + return item['name'].toString().toLowerCase().contains( + filter.toLowerCase(), + ); + }).toList(); + }, + itemAsString: (val) => val['insurance_plan_type'].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( + errorStyle: const TextStyle(fontSize: 0, height: 0.0), + filled: true, + fillColor: + Colors.white, // 👈 makes the dropdown input white + isDense: true, + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(5), + borderSide: const BorderSide( + color: Color(0xFFE2E8F0), + width: 0.5, + ), + ), + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(5), + borderSide: const BorderSide( + color: Color(0xFFE2E8F0), + width: 0.5, + ), + ), + contentPadding: EdgeInsets.symmetric( + horizontal: 8, + 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( + autofocus: true, + decoration: InputDecoration( + contentPadding: EdgeInsets.all(1), + filled: true, + fillColor: Colors.white, + hintText: "Search Plan 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 + ), + ), + ), + + 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['insurance_plan_type'].toString(), + style: GoogleFonts.inter(fontSize: 12, color: Colors.black), + ), + ); + }, + // searchFieldProps: TextFieldProps( + // decoration: InputDecoration( + // filled: true, + // fillColor: Colors.white, + // hintText: "Search Plan Type...", + // enabledBorder: OutlineInputBorder( + // borderSide: BorderSide( + // color: Colors.white, + // ), // 👈 Normal border + // ), + // focusedBorder: OutlineInputBorder( + // borderSide: BorderSide( + // color: Colors.white, + // width: 1.5, + // ), // 👈 Focused border + // ), + // ), + // ), + // constraints: BoxConstraints(), + ), + + onChanged: (val) { + if (val != null) { + print("Selected ClaimsType : ${val['insurance_plan_type']}"); + print("Id: ${val['id']}"); + selectedInsPlanType = val['id']; + // controllers['agentId']?.text = val['agent_code']; + // agentId = agent['id']; + } + }, + ), + ), + ], + ); + } + + Widget buildPaymentMode(context) { + Map? selectedVehicle = filteredPaymentModeData.firstWhere( + (item) => item['id'].toString() == selectedPaymentMode, + orElse: () => {}, + ); + + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text("Payment Mode", style: _textStyle), + SizedBox(height: 10), + Container( + color: Colors.white, + width: ResponsiveLayout.isMobile(context) + ? null + : MediaQuery.of(context).size.width * 0.18, + height: 35, + child: DropdownSearch>( + key: dropDownSelectPaymentModeKey, + selectedItem: selectedVehicle.isNotEmpty ? selectedVehicle : null, + // items: (filter, infiniteScrollProps) { + // return filteredInsuranceData; + // }, + items: (filter, infiniteScrollProps) async { + if (filter.isEmpty) { + return filteredPaymentModeData; + } + return filteredPaymentModeData.where((item) { + return item['name'].toString().toLowerCase().contains( + filter.toLowerCase(), + ); + }).toList(); + }, + itemAsString: (val) => val['value'].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 Payment Mode", + ).copyWith( + errorStyle: const TextStyle(fontSize: 0, height: 0.0), + filled: true, + fillColor: + Colors.white, // 👈 makes the dropdown input white + isDense: true, + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(5), + borderSide: const BorderSide( + color: Color(0xFFE2E8F0), + width: 0.5, + ), + ), + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(5), + borderSide: const BorderSide( + color: Color(0xFFE2E8F0), + width: 0.5, + ), + ), + contentPadding: EdgeInsets.symmetric( + horizontal: 8, + 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( + autofocus: true, + decoration: InputDecoration( + contentPadding: EdgeInsets.all(1), + filled: true, + fillColor: Colors.white, + hintText: "Search Payment Mode ...", + 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['value'].toString(), + style: GoogleFonts.inter(fontSize: 12, color: Colors.black), + ), + ); + }, + ), + + onChanged: (val) { + if (val != null) { + print("Selected value : ${val['value']}"); + print("Id: ${val['id']}"); + selectedPaymentMode = val['id']; + // controllers['agentId']?.text = val['agent_code']; + // agentId = agent['id']; + } + }, + ), + ), + ], + ); + } + + Widget buildBroker(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( + color: Colors.white, + width: ResponsiveLayout.isMobile(context) + ? null + : MediaQuery.of(context).size.width * 0.18, + height: 35, + child: DropdownSearch>( + key: dropDownKeyBroker, + selectedItem: selectedBrokers.isNotEmpty ? selectedBrokers : null, + // items: (filter, infiniteScrollProps) { + // return filteredInsuranceData; + // }, + items: (filter, infiniteScrollProps) async { + if (filter.isEmpty) { + return filteredBrokerData; + } + return filteredBrokerData.where((item) { + return item['name'].toString().toLowerCase().contains( + filter.toLowerCase(), + ); + }).toList(); + }, + 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 Broker", + ).copyWith( + errorStyle: const TextStyle(fontSize: 0, height: 0.0), + filled: true, + fillColor: + Colors.white, // 👈 makes the dropdown input white + isDense: true, + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(5), + borderSide: const BorderSide( + color: Color(0xFFE2E8F0), + width: 0.5, + ), + ), + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(5), + borderSide: const BorderSide( + color: Color(0xFFE2E8F0), + width: 0.5, + ), + ), + contentPadding: EdgeInsets.symmetric( + horizontal: 8, + 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( + autofocus: true, + decoration: InputDecoration( + contentPadding: EdgeInsets.all(1), + 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), + ), + ); + }, + ), + + onChanged: (val) { + if (val != null) { + print("Selected ClaimsType : ${val['id']}"); + print("Id: ${val['id']}"); + selectedBroker = val['id']; + } + }, + ), + ), + ], + ); + } + + // ------------------- STyle --------------------------------- + + static final TextStyle _textStyle = TextStyle( + fontSize: 11, + color: Color(0XFF334155), + fontWeight: FontWeight.w500, + ); +} diff --git a/lib/presentation/screens/staff/Enquiry/enquiry_inline_Edit_17Nov.dart b/lib/presentation/screens/staff/Enquiry/enquiry_inline_Edit_17Nov.dart index 54956ed..7e861b2 100644 --- a/lib/presentation/screens/staff/Enquiry/enquiry_inline_Edit_17Nov.dart +++ b/lib/presentation/screens/staff/Enquiry/enquiry_inline_Edit_17Nov.dart @@ -39,13 +39,13 @@ import '../../../themes/indicators/text_field_theme_inline_editor.dart' import '../../../widgets/custom_Stdate_EnDate_Filter.dart'; import '../assignStaff.dart'; -class EnquiryListStaffInline extends ConsumerStatefulWidget { - const EnquiryListStaffInline({super.key}); +class EnquiryListStaffInlineTST extends ConsumerStatefulWidget { + const EnquiryListStaffInlineTST({super.key}); @override - ConsumerState createState() => EnquiryStaffState(); + ConsumerState createState() => EnquiryStaffState(); } -class EnquiryStaffState extends ConsumerState { +class EnquiryStaffState extends ConsumerState { int currentPage = 1; int itemsPerPage = 10; late ApiService apiService; diff --git a/lib/presentation/screens/staff/Enquiry/enquiry_inline_list.dart b/lib/presentation/screens/staff/Enquiry/enquiry_inline_list.dart index d761fa4..dc071a0 100644 --- a/lib/presentation/screens/staff/Enquiry/enquiry_inline_list.dart +++ b/lib/presentation/screens/staff/Enquiry/enquiry_inline_list.dart @@ -1,3 +1,4 @@ +import 'dart:async'; import 'dart:ui'; import 'package:file_picker/file_picker.dart'; import 'package:flutter/services.dart'; @@ -9,6 +10,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/dashboard/dashboard.dart'; import 'package:nhance_partner/presentation/screens/staff/Enquiry/quotationPopUpAccptReject.dart'; import 'package:nhance_partner/presentation/screens/staff/Enquiry/raise_enq_form.dart'; import 'package:nhance_partner/presentation/screens/staff/Enquiry/tabs/tab.dart'; @@ -34,11 +36,13 @@ 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/side_Drawer_Panel.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'; +import 'multi_file_list.dart'; class EnquiryListStaffInline extends ConsumerStatefulWidget { const EnquiryListStaffInline({super.key}); @@ -56,6 +60,8 @@ class EnquiryStaffState extends ConsumerState { dynamic dashboardKey; dynamic SelectedStatus; dynamic SelectedStaffId; + String? selectedFileNames; + PlatformFile? docUploadedFile; dynamic handlerId; String? _token; final _formKey = GlobalKey(); @@ -101,12 +107,20 @@ class EnquiryStaffState extends ConsumerState { List> getInsurersData = []; List> filteredInsurersData = []; + List> getPaymentModeData = []; + List> filteredPaymentModeData = []; + + List> getInsuranceTypeData = []; + List> filteredInsuranceData = []; + String? selectedVehicleType; String? selectedInsurer; + String? selectedInsPlanType; String? selectedAgent; PlatformFile? docUploadedRCFile; PlatformFile? docUploadedIDProof; PlatformFile? docUploadedPrevPolicy; + String? selectedPaymentMode; String? selectedRCFile; String? selectedIdProof; @@ -157,6 +171,10 @@ class EnquiryStaffState extends ConsumerState { dropDownKeyInsurer = GlobalKey>>(); final GlobalKey>> dropDownKeyAgent = GlobalKey>>(); + + final GlobalKey>> + dropDownSelectPaymentModeKey = + GlobalKey>>(); //---------------------- Assign Staff starts -------------------------------- // final GlobalKey>> @@ -175,11 +193,15 @@ class EnquiryStaffState extends ConsumerState { String? selectedStaff; String? selectedRegNum; String? selectedStaffName; + List> getBrokerData = []; List> filteredBrokerData = []; String? selectedBroker; final ScrollController _horizontalScrollController = ScrollController(); + late Timer timer; + + bool isActionable = false; //---------------------- Assign Staff Ends -------------------------------- // //---------------------- Enquiry TAB Initializations Ends -------------------------------- // @@ -189,7 +211,7 @@ class EnquiryStaffState extends ConsumerState { super.initState(); apiService = ApiService(); - + timer = Timer.periodic(Duration(seconds: 3), (Timer t) => callRefresh()); for (String field in tabHeader) { controllers[field] = TextEditingController(); } @@ -246,7 +268,9 @@ class EnquiryStaffState extends ConsumerState { getVehicleType(); getInsurers(); + getInsuranceType(); getBroker(); + getPaymentMode(); } Future _initializeToken() async { @@ -254,6 +278,14 @@ class EnquiryStaffState extends ConsumerState { print("APISERTOKEN - $_token"); } + void callRefresh() { + print('Call Refresh'); + if (!isActionable) { + print('Call Refresh isActionable'); + refrshfilterDateRange(); + } + } + void refresh() { final id = ref.read(managerIdProvider); final roleId = ref.read(userRoleProvider); @@ -345,6 +377,39 @@ class EnquiryStaffState extends ConsumerState { } } + Future getInsuranceType() async { + print('getClaimList called'); + setState(() { + isLoading = true; + }); + + try { + final response = await apiService.fetchMasterDropDown('InsuranceType'); + + if (response['status'] == 200) { + print('getInsuranceTypeData - ${response['data']}'); + setState(() { + getInsuranceTypeData = List>.from( + response['data'], + ); + print('API Data - $getInsuranceTypeData'); + + filteredInsuranceData = List.from(getInsuranceTypeData); + print('originalData - $filteredInsuranceData'); + }); + } else { + getInsuranceTypeData = []; + filteredInsuranceData = []; + } + } catch (e) { + print('Exception occurred: $e'); + } finally { + setState(() { + isLoading = false; + }); + } + } + Future getBroker() async { print('getBroker called'); setState(() { @@ -376,6 +441,39 @@ class EnquiryStaffState extends ConsumerState { } } + Future getPaymentMode() async { + print('getPaymentMode called'); + setState(() { + isLoading = true; + }); + + try { + final response = await apiService.fetchMasterDropDown('PaymentMode'); + + if (response['status'] == 200) { + print('getPaymentModeData - ${response['data']}'); + setState(() { + getPaymentModeData = List>.from( + response['data'], + ); + print('API Data - $getPaymentModeData'); + + filteredPaymentModeData = List.from(getPaymentModeData); + print('originalData - $filteredPaymentModeData'); + }); + } else { + getPaymentModeData = []; + filteredPaymentModeData = []; + } + } catch (e) { + print('Exception occurred: $e'); + } finally { + setState(() { + isLoading = false; + }); + } + } + Future getStaffDetailsForEnquiryAssignment(int id) async { print('getStaffDetailsForEnquiryAssignment called By handler'); setState(() { @@ -640,6 +738,42 @@ class EnquiryStaffState extends ConsumerState { setState(() {}); } + Future handlePaymentMode(id, quotId) async { + Map data = { + "quotation_id": quotId, + "payment_mode_id": id, + "updated_by": userId, + }; + + final path = 'quotation/updateQuoteData'; + + final res = await apiService.createUserData(data, path); + + print("handlePaymentMode - $res"); + + if (res['status'] == 'success') { + print("handlePaymentModesuccess"); + refrshfilterDateRange(); + } + } + + Future handleInsurancePlanTypeMode(id, quotId) async { + Map data = { + "quotation_id": quotId, + "insurance_plan_type_id": id, + "updated_by": userId, + }; + + final path = 'quotation/updateQuoteData'; + + final res = await apiService.createUserData(data, path); + print("handleINSMode - $res"); + if (res['status'] == 'success') { + print("handleINSsuccess"); + refrshfilterDateRange(); + } + } + void handleSave(dynamic id) { print('SAVE -- $id'); // final ids = int.tryParse(id.toString()) ?? 0; @@ -1697,6 +1831,146 @@ class EnquiryStaffState extends ConsumerState { ); } + Widget buildPaymentMode(context) { + Map? selectedVehicle = filteredPaymentModeData.firstWhere( + (item) => item['id'].toString() == selectedPaymentMode, + orElse: () => {}, + ); + + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text("Payment Mode", style: _textStyle), + // SizedBox(height: 10), + SizedBox( + // color: Colors.red, + // color: Colors.yellow, + width: 200, + // height: 30, + // width: ResponsiveLayout.isMobile(context) + // ? null + // : MediaQuery.of(context).size.width * 0.18, + // height: 35, + child: DropdownSearch>( + key: dropDownSelectPaymentModeKey, + selectedItem: selectedVehicle.isNotEmpty ? selectedVehicle : null, + // items: (filter, infiniteScrollProps) { + // return filteredInsuranceData; + // }, + items: (filter, infiniteScrollProps) async { + if (filter.isEmpty) { + return filteredPaymentModeData; + } + return filteredPaymentModeData.where((item) { + return item['name'].toString().toLowerCase().contains( + filter.toLowerCase(), + ); + }).toList(); + }, + itemAsString: (val) => val['value'].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 Payment Mode", + ).copyWith( + filled: true, + fillColor: + Colors.white, // 👈 makes the dropdown input white + isDense: true, + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(5), + borderSide: const BorderSide( + color: Color(0xFFE2E8F0), + width: 0.5, + ), + ), + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(5), + borderSide: const BorderSide( + color: Color(0xFFE2E8F0), + width: 0.5, + ), + ), + contentPadding: EdgeInsets.symmetric( + horizontal: 8, + vertical: 6, + ), + ), + ), + popupProps: PopupProps.dialog( + fit: FlexFit.loose, + constraints: BoxConstraints(maxHeight: 400, maxWidth: 300), + // constraints: BoxConstraints(maxHeight: 250), + dialogProps: DialogProps(backgroundColor: Colors.white), + // menuProps: MenuProps( + // backgroundColor: + // Colors.white, // 👈 sets dropdown background to white + // ), + showSearchBox: true, + searchFieldProps: TextFieldProps( + autofocus: true, + decoration: InputDecoration( + contentPadding: EdgeInsets.all(1), + filled: true, + fillColor: Colors.white, + hintText: "Search Payment Mode ...", + 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['value'].toString(), + style: GoogleFonts.inter(fontSize: 12, color: Colors.black), + ), + ); + }, + ), + + onChanged: (val) { + if (val != null) { + print("Selected value : ${val['value']}"); + print("Id: ${val['id']}"); + selectedPaymentMode = val['id']; + // controllers['agentId']?.text = val['agent_code']; + // agentId = agent['id']; + } + }, + ), + ), + ], + ); + } + Widget buildResponsiveUploadField({ required String label, required String? hintText, @@ -2497,6 +2771,76 @@ class EnquiryStaffState extends ConsumerState { } } + Future uploadPolicyPDF({ + required Map data, + required PlatformFile? file, + }) async { + if (_token == null) throw Exception("Token missing. Please login."); + + final uri = Uri.parse('${Env.apiUrl}policy/uploadPolicyFile'); + final request = http.MultipartRequest('POST', uri); + + request.headers['Authorization'] = 'Bearer $_token'; + request.headers['app-signature'] = Env.App_Signature; + + print("🔥 Upload Request Data: $data"); + + // 1️⃣ Add text fields (except file) + data.forEach((key, value) { + if (value != null && key != 'policy_pdf_file_name') { + request.fields[key] = value.toString(); + print("➡️ Field Added: $key = ${request.fields[key]}"); + } + }); + + // 2️⃣ Attach file + if (file != null) { + if (file.bytes != null) { + request.files.add( + http.MultipartFile.fromBytes( + 'policy_pdf_file_name', + file.bytes!, + filename: file.name, + ), + ); + } else if (file.path != null) { + request.files.add( + await http.MultipartFile.fromPath( + 'policy_pdf_file_name', + file.path!, + filename: file.name, + ), + ); + } + print("📎 File attached: ${file.name}"); + } else { + print("⚠️ No file selected!"); + } + + // 3️⃣ Submit request + try { + final response = await http.Response.fromStream(await request.send()); + + print("📥 Response Code: ${response.statusCode}"); + print("📥 Response Body: ${response.body}"); + + if (response.statusCode == 200 || response.statusCode == 201) { + ToastHelper.showSuccessToast(context, "Policy PDF Uploaded!"); + refrshfilterDateRange(); + return; + } + + if (response.statusCode == 403) { + await apiService.clearLocalStorageAndRedirect(); + return; + } + + ToastHelper.showErrorToast(context, "Upload failed!"); + } catch (e) { + print("❌ Error uploading file: $e"); + } + } + //---------------------- Enquiry TAB Logins End -------------------------------- // @override Widget build(BuildContext context) { @@ -3152,7 +3496,7 @@ class EnquiryStaffState extends ConsumerState { cells: [ DataCell( SizedBox( - width: 80, + width: 70, child: Row( children: [ // Text(item['created_on'], style: _dataBold), @@ -3168,7 +3512,7 @@ class EnquiryStaffState extends ConsumerState { DataCell( SizedBox( // flex: 1, - width: 120, + width: 110, child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.center, @@ -3188,7 +3532,7 @@ class EnquiryStaffState extends ConsumerState { // Agent Name DataCell( SizedBox( - width: 150, + width: 100, child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.center, @@ -3208,7 +3552,7 @@ class EnquiryStaffState extends ConsumerState { // Insurer Name DataCell( SizedBox( - width: 80, + width: 60, child: Text( item['insurer_short_name'] ?? '-', style: _dataBoldthm2, @@ -3237,64 +3581,64 @@ class EnquiryStaffState extends ConsumerState { child: Builder( builder: (buttonContext) => Material( color: Colors.white, - child: InkWell( - // hoverColor: 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, - // overlay.size.width - position.dx, - 0, - ), - items: [ - PopupMenuItem( - child: Column( - mainAxisSize: MainAxisSize.min, - children: _buildPopupDownload_BTN( - buttonContext, - item, - item['id'], - item['reg_no'], - ), - ), - ), - ], - color: Colors.white, - ); - }, - child: Tooltip( - message: - 'Click and Download Enquiry Files', // what appears on hover/long press - waitDuration: const Duration(milliseconds: 500), // optional - showDuration: const Duration(seconds: 2), // optional - child: Container( - padding: EdgeInsets.all(4.0), - margin: EdgeInsets.symmetric(vertical: 3, horizontal: 2), - decoration: BoxDecoration( - color: Color(0xFFF9FAFB), - border: Border.all(color: Color(0xFFE2E8F0)), - borderRadius: BorderRadius.circular(5), - ), - child: Text(item['reg_no'] ?? '-', style: _dataBoldthm3), + // child: InkWell( + // // hoverColor: 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, + // // overlay.size.width - position.dx, + // 0, + // ), + // items: [ + // PopupMenuItem( + // child: Column( + // mainAxisSize: MainAxisSize.min, + // children: _buildPopupDownload_BTN( + // buttonContext, + // item, + // item['id'], + // item['reg_no'], + // ), + // ), + // ), + // ], + // color: Colors.white, + // ); + // }, + child: Tooltip( + message: + 'Click and Download Enquiry Files', // what appears on hover/long press + waitDuration: const Duration(milliseconds: 500), // optional + showDuration: const Duration(seconds: 2), // optional + child: Container( + padding: EdgeInsets.all(4.0), + margin: EdgeInsets.symmetric(vertical: 3, horizontal: 2), + decoration: BoxDecoration( + color: Color(0xFFF9FAFB), + border: Border.all(color: Color(0xFFE2E8F0)), + borderRadius: BorderRadius.circular(5), ), + child: Text(item['reg_no'] ?? '-', style: _dataBoldthm3), ), - // child: Text(item['reg_no'] ?? '-', style: _dataBold), ), + // child: Text(item['reg_no'] ?? '-', style: _dataBold), + // ), ), ), ), @@ -3302,39 +3646,9 @@ class EnquiryStaffState extends ConsumerState { DataCell( SizedBox( - width: 50.0, + width: 70.0, child: Row( children: [ - // Tooltip( - // message: - // 'Email : ${item["email"]}\n' - // 'Mobile : ${item["mobile"]}\n' - // 'Remarks : ${item["remarks"]}', - // decoration: BoxDecoration( - // color: Colors.white, // background color - // borderRadius: BorderRadius.circular(6), - // border: Border.all(color: Colors.grey, width: 1), - // ), - // textStyle: GoogleFonts.inter( - // color: Colors.black, - // fontSize: 12, - // ), - // - // padding: const EdgeInsets.symmetric( - // horizontal: 8, - // vertical: 4, // reduce tooltip height - // ), - // margin: const EdgeInsets.only( - // left: 10, - // ), // tooltip distance from widget - // waitDuration: Duration(milliseconds: 500), - // showDuration: Duration(seconds: 2), - // child: Icon( - // Icons.info_outline_rounded, - // size: 15, - // color: Colors.blue, - // ), - // ), Tooltip( richMessage: WidgetSpan( child: Container( @@ -3386,11 +3700,13 @@ class EnquiryStaffState extends ConsumerState { tooltip: "Edit", onPressed: () { setState(() { + isActionable = false; bool isOpen = rowExpanded[id] ?? false; rowExpanded.clear(); // close all if (!isOpen) { + isActionable = true; rowExpanded[id] = true; // open only if previously closed } @@ -3399,6 +3715,60 @@ class EnquiryStaffState extends ConsumerState { handleEdit(item); }, ), + Builder( + builder: (buttonContext) => Material( + color: Colors.white, + child: InkWell( + // hoverColor: 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, + // overlay.size.width - position.dx, + 0, + ), + items: [ + PopupMenuItem( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [MultipleFileWidget(enqId: selectId)], + ), + ), + ], + color: Colors.white, + ); + }, + child: Tooltip( + message: + 'Click and Upload / Download Enquiry Files', // what appears on hover/long press + waitDuration: const Duration( + milliseconds: 500, + ), // optional + showDuration: const Duration(seconds: 2), // optional + child: Icon( + Icons.file_upload_outlined, + size: 15, + color: Colors.green, + ), + ), + ), + ), + ), ], ), ), @@ -3419,55 +3789,499 @@ class EnquiryStaffState extends ConsumerState { // ), // Premium + // DataCell( + // // (item['status'] == 'Proposal Created' || + // // item['status'] == 'Proposal Rejected') + // // ? SizedBox( + // // width: 80, + // // child: 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, + // // ), + // // ), + // // ), + // // ) + // // : + // SizedBox( + // width: 90, + // child: Text( + // item['premium_amount']?.toString() ?? '-', + // style: _dataBold, + // ), + // ), + // ), + + // // Payment Mode + // DataCell( + // // Material(child: buildPaymentMode(context)), + // SizedBox( + // // width: 80, + // // child: Text(item['payment_mode_value'] ?? '-', style: _dataBold), + // child: buildPaymentMode(context), + // ), + // ), DataCell( - // (item['status'] == 'Proposal Created' || - // item['status'] == 'Proposal Rejected') - // ? SizedBox( - // width: 80, - // child: 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, - // ), - // ), - // ), - // ) - // : SizedBox( - width: 90, - child: Text( - item['premium_amount']?.toString() ?? '-', - style: _dataBold, - ), + height: 30, + width: 120, + child: (item['status'] == 'Awaiting Proposal') + ? Text('-') + : Builder( + builder: (cellContext) => + DropdownSearch>( + key: ValueKey(item['id']), + + selectedItem: () { + try { + return filteredPaymentModeData.firstWhere( + (mode) => + mode['id'].toString() == + item['payment_mode_id'].toString(), + orElse: () => {}, + ); + } catch (e) { + return null; + } + }(), + + items: (filter, infiniteScrollProps) async { + if (filter.isEmpty) { + return filteredPaymentModeData; + } + return filteredPaymentModeData.where((paymentMode) { + return paymentMode['value'] + .toString() + .toLowerCase() + .contains(filter.toLowerCase()); + }).toList(); + }, + + itemAsString: (val) => val['value'].toString(), + compareFn: (item, selectedItem) => + item['id'] == selectedItem['id'], + suffixProps: DropdownSuffixProps( + // make sure the dropdown button is visible + dropdownButtonProps: DropdownButtonProps( + isVisible: true, + padding: + EdgeInsets.zero, // remove default padding + constraints: const BoxConstraints( + // shrink icon tap area + minWidth: 12, + minHeight: 12, + ), + iconSize: 8, // smaller icon + color: Colors.black45, + // icon: const Icon(Icons.arrow_drop_down), + ), + ), + + dropdownBuilder: (context, selectedItem) { + // Check if selectedItem is null or empty map + bool isEmpty = + selectedItem == null || + selectedItem.isEmpty || + selectedItem['value'] == null; + + return Container( + padding: EdgeInsets.zero, + // padding: EdgeInsets.symmetric(horizontal: 2), + alignment: Alignment.centerLeft, + child: Text( + isEmpty + ? "Select Payment Mode" + : selectedItem['value'].toString(), + style: GoogleFonts.poppins( + fontSize: 11, + color: isEmpty + ? Colors + .grey + .shade500 // Grey for hint + : Colors.black, // Blue for selected + // : Color(0XFF6366F1), // Blue for selected + fontWeight: isEmpty + ? FontWeight.w400 + : FontWeight.w400, + ), + overflow: TextOverflow.ellipsis, + maxLines: 1, + softWrap: false, + ), + ); + }, + decoratorProps: DropDownDecoratorProps( + decoration: + AppInputDecorations.dropdownDecoration( + // label: "Select Payment Mode", + ).copyWith( + filled: true, + fillColor: Colors + .white, // 👈 makes the dropdown input white + isDense: true, + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(5), + borderSide: const BorderSide( + color: Color(0xFFE2E8F0), + width: 0.5, + ), + ), + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(5), + borderSide: const BorderSide( + //color: Color(0xFFFFFFFF), + color: Color(0xFFE2E8F0), + width: 0.5, + ), + ), + contentPadding: EdgeInsets.symmetric( + horizontal: 1, + vertical: 6, + ), + ), + ), + // 🔥 Use PopupProps.menu - works better in tables + popupProps: PopupProps.menu( + fit: FlexFit.loose, + constraints: BoxConstraints( + maxHeight: 300, + minWidth: 200, + ), + menuProps: MenuProps( + backgroundColor: Colors.white, + elevation: 8, + borderRadius: BorderRadius.circular(8), + ), + showSearchBox: true, + searchFieldProps: TextFieldProps( + autofocus: true, + decoration: InputDecoration( + contentPadding: EdgeInsets.all(8), + filled: true, + fillColor: Color(0xFFF8FAFC), + hintText: "Search...", + hintStyle: GoogleFonts.inter( + fontSize: 11, + color: Colors.grey, + ), + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(5), + borderSide: BorderSide( + color: Color(0xFFE2E8F0), + ), + ), + ), + ), + itemBuilder: + (context, item, isDisabled, isSelected) { + return Container( + color: isSelected + ? Colors.blue.withOpacity(0.1) + : Colors.white, + padding: EdgeInsets.symmetric( + horizontal: 5, + vertical: 5, + ), + child: Text( + item['value'].toString(), + style: GoogleFonts.inter( + fontSize: 11, + color: Colors.black87, + fontWeight: isSelected + ? FontWeight.w600 + : FontWeight.normal, + ), + ), + ); + }, + ), + + onChanged: (val) { + dynamic id; + if (val != null) { + setState(() { + item['payment_mode_id'] = val['id'].toString(); + item['payment_mode'] = val['value'].toString(); + id = val['id'].toString(); + }); + } + handlePaymentMode(id, item['quotation_id']); + }, + ), + ), + ), + ), + DataCell( + SizedBox( + width: 120, + height: 30, + child: (item['status'] == 'Awaiting Proposal') + ? Text('-') + : Builder( + builder: (cellContext) => DropdownSearch>( + key: ValueKey(item['id']), + selectedItem: () { + try { + return filteredInsuranceData.firstWhere( + (mode) => + mode['insurance_plan_type'] + .toString() + .trim() + .toLowerCase() == + item['insurance_plan_type'] + .toString() + .trim() + .toLowerCase(), + orElse: () => {}, + ); + } catch (e) { + return null; + } + }(), + + items: (filter, infiniteScrollProps) async { + if (filter.isEmpty) { + return filteredInsuranceData; + } + return filteredInsuranceData.where((paymentMode) { + return paymentMode['insurance_plan_type'] + .toString() + .toLowerCase() + .contains(filter.toLowerCase()); + }).toList(); + }, + + itemAsString: (val) => + val['insurance_plan_type'].toString(), + compareFn: (item, selectedItem) => + item['id'] == selectedItem['id'], + suffixProps: DropdownSuffixProps( + // make sure the dropdown button is visible + dropdownButtonProps: DropdownButtonProps( + isVisible: true, + padding: EdgeInsets.zero, // remove default padding + constraints: const BoxConstraints( + // shrink icon tap area + minWidth: 12, + minHeight: 12, + ), + iconSize: 8, // smaller icon + color: Colors.black45, + // icon: const Icon(Icons.arrow_drop_down), + ), + ), + + dropdownBuilder: (context, selectedItem) { + // Check if selectedItem is null or empty map + bool isEmpty = + selectedItem == null || + selectedItem.isEmpty || + selectedItem['insurance_plan_type'] == null; + + return Container( + padding: EdgeInsets.zero, + // padding: EdgeInsets.symmetric(horizontal: 2), + alignment: Alignment.centerLeft, + child: Text( + isEmpty + ? "Select Insurer" + : selectedItem['insurance_plan_type'] + .toString(), + style: GoogleFonts.poppins( + fontSize: 11, + color: isEmpty + ? Colors + .grey + .shade500 // Grey for hint + : Colors.black, // Blue for selected + // : Color(0XFF6366F1), // Blue for selected + fontWeight: isEmpty + ? FontWeight.w400 + : FontWeight.w400, + ), + overflow: TextOverflow.ellipsis, + maxLines: 1, + softWrap: false, + ), + ); + }, + decoratorProps: DropDownDecoratorProps( + decoration: + AppInputDecorations.dropdownDecoration( + // label: "Select Payment Mode", + ).copyWith( + filled: true, + fillColor: Colors + .white, // 👈 makes the dropdown input white + isDense: true, + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(5), + borderSide: const BorderSide( + color: Color(0xFFE2E8F0), + width: 0.5, + ), + ), + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(5), + borderSide: const BorderSide( + //color: Color(0xFFFFFFFF), + color: Color(0xFFE2E8F0), + width: 0.5, + ), + ), + contentPadding: EdgeInsets.symmetric( + horizontal: 1, + vertical: 6, + ), + ), + ), + // 🔥 Use PopupProps.menu - works better in tables + popupProps: PopupProps.menu( + fit: FlexFit.loose, + constraints: BoxConstraints( + maxHeight: 300, + minWidth: 200, + ), + menuProps: MenuProps( + backgroundColor: Colors.white, + elevation: 8, + borderRadius: BorderRadius.circular(8), + ), + showSearchBox: true, + searchFieldProps: TextFieldProps( + autofocus: true, + decoration: InputDecoration( + contentPadding: EdgeInsets.all(8), + filled: true, + fillColor: Color(0xFFF8FAFC), + hintText: "Search...", + hintStyle: GoogleFonts.inter( + fontSize: 11, + color: Colors.grey, + ), + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(5), + borderSide: BorderSide(color: Color(0xFFE2E8F0)), + ), + ), + ), + itemBuilder: (context, item, isDisabled, isSelected) { + return Container( + color: isSelected + ? Colors.blue.withOpacity(0.1) + : Colors.white, + padding: EdgeInsets.symmetric( + horizontal: 5, + vertical: 5, + ), + child: Text( + item['insurance_plan_type'].toString(), + style: GoogleFonts.inter( + fontSize: 11, + color: Colors.black87, + fontWeight: isSelected + ? FontWeight.w600 + : FontWeight.normal, + ), + ), + ); + }, + ), + + onChanged: (val) { + dynamic id; + if (val != null) { + setState(() { + // item['payment_mode_id'] = val['id'].toString(); + item['insurance_plan_type'] = + val['insurance_plan_type'].toString(); + id = val['id'].toString(); + }); + } + handleInsurancePlanTypeMode(id, item['quotation_id']); + }, + ), + ), ), ), - // Payment Mode - DataCell( - SizedBox( - width: 100, - child: Text(item['payment_mode_value'] ?? '-', style: _dataBold), - ), - ), + // DataCell( + // buildPaymentMode(context), + // // Builder( + // // builder: (cellContext) => buildPaymentMode(cellContext), + // // // SizedBox(width: 200, child: buildPaymentMode(cellContext)), + // // ), + // ), // Policy Number DataCell( SizedBox( + height: 30, width: 90, - child: Text( - item['policy_number'] ?? '-', - style: _dataBold, - softWrap: true, - maxLines: 3, - ), + child: (item['status'] == 'Awaiting Proposal') + ? Text('-') + : ThemedUploadField( + // hintText: item['policy_pdf_file_name'] ?? "Upload Document", + hintText: + selectedFileNames ?? + item['policy_pdf_file_name'] ?? + "Upload Document", + // txtheight: 30, + padHorizontal: 4, + padVertical: 5, + fontSZ: 11, + borderCirculr: 5, + txtwidth: ResponsiveLayout.isMobile(context) + ? null + : MediaQuery.of(context).size.width * 0.18, + borderColor: Color(0xFFE2E8F0), + + // highlightColor: Color(0xFF50A398), + // 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 + } + + print("Picked file: $fileName (${file.size} bytes)"); + setState(() { + docUploadedFile = file; + }); + await uploadPolicyPDF( + file: docUploadedFile, + data: { + "quotation_id": item["quotation_id"], + "manager_id": managerId, + "created_by": userId, + "updated_by": userId, + // "id": item["id"], // ONLY while updating + }, + ); + // if (selectedBrokerName == 'Nhance') { + // handleUploadPolicy(); + // } + }, + ), ), ), @@ -3498,8 +4312,8 @@ class EnquiryStaffState extends ConsumerState { waitDuration: const Duration(milliseconds: 500), // optional showDuration: const Duration(seconds: 2), // optional child: Container( - padding: EdgeInsets.all(4.0), - margin: EdgeInsets.symmetric(vertical: 3), + padding: EdgeInsets.symmetric(vertical: 2.0), + margin: EdgeInsets.symmetric(vertical: 2), decoration: BoxDecoration( // color: Color(0xFFF9FAFB), // border: Border.all(color: Color(0xFFE2E8F0)), @@ -3514,7 +4328,7 @@ class EnquiryStaffState extends ConsumerState { Text( item['status'] ?? '-', style: GoogleFonts.inter( - fontSize: 11, + fontSize: 10, color: Color(0XFF1e293b), // color: statusTextColors[status] ?? Colors.black, fontWeight: FontWeight.w500, @@ -3547,8 +4361,9 @@ class EnquiryStaffState extends ConsumerState { DataColumn(label: Text('Vehicle No *', style: _headerStyle)), DataColumn(label: Text('Action', style: _headerStyle)), // const DataColumn(label: Text('Assigned Date', style: _headerStyle)), - DataColumn(label: Text('Premium', style: _headerStyle)), + // DataColumn(label: Text('Premium', style: _headerStyle)), DataColumn(label: Text('Payment Mode', style: _headerStyle)), + DataColumn(label: Text('Plan Type', style: _headerStyle)), DataColumn(label: Text('Policy Number', style: _headerStyle)), DataColumn(label: Text('Status', style: _headerStyle)), ]; @@ -3562,16 +4377,18 @@ class EnquiryStaffState extends ConsumerState { children: [ _buildHeaderCell('Received Date', 110), _buildHeaderCell('Assigned To *', 130), - _buildHeaderCell('Partner *', 160), + _buildHeaderCell('Partner *', 100), // _buildHeaderCell('Broker *', 100), _buildHeaderCell('Insurer *', 90), _buildHeaderCell('Insured Name *', 110), _buildHeaderCell('Vehicle No *', 90), - _buildHeaderCell('Action', 60), + _buildHeaderCell('Action', 80), // _buildHeaderCell('Assigned Date', 100), - _buildHeaderCell('Premium', 100), - _buildHeaderCell('Payment Mode', 120), + // _buildHeaderCell('Premium', 100), + _buildHeaderCell('Payment Mode', 130), + _buildHeaderCell('Plan Type', 130), + _buildHeaderCell('Policy Number', 120), _buildHeaderCell('Status', 100), ], diff --git a/lib/presentation/screens/staff/Enquiry/multi_file_list.dart b/lib/presentation/screens/staff/Enquiry/multi_file_list.dart new file mode 100644 index 0000000..007072c --- /dev/null +++ b/lib/presentation/screens/staff/Enquiry/multi_file_list.dart @@ -0,0 +1,293 @@ +import 'dart:convert'; + +import 'package:file_picker/file_picker.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/src/widgets/framework.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:google_fonts/google_fonts.dart'; +import 'package:http/http.dart' as http; +import 'package:nhance_partner/presentation/themes/indicators/mutli_FileUpload.dart'; + +import '../../../../core/config/env.dart'; +import '../../../../core/services/api_service.dart'; +import '../../../../data/services/auth_service.dart'; +import '../../../../data/utils/toastNotification.dart'; +import '../../../themes/indicators/multi_fileUploadService.dart'; + +class MultipleFileWidget extends ConsumerStatefulWidget { + dynamic enqId; + MultipleFileWidget({super.key, required this.enqId}); + + @override + ConsumerState createState() => MultipleFileWidgetState(); +} + +class MultipleFileWidgetState extends ConsumerState { + List uploadedFiles = []; + final fileService = MultiFileUploadService(); + late ApiService apiService; + bool isLoading = false; + dynamic _token; + + List> getUploadedData = []; + List> originalData = []; + List> filteredData = []; + + @override + void initState() { + super.initState(); + fileService.clearAll(); + apiService = ApiService(); + _initializeToken(); + getList(); + } + + Future _initializeToken() async { + _token = await AuthService.getToken(); + print("APISERTOKEN - $_token"); + } + + Future getList() async { + setState(() { + isLoading = true; + }); + + try { + final response = await apiService.fetchUploadEnquiryFiles(widget.enqId); + + if (response['status'] == 'success') { + print('E113 => getfetchUploadEnquiryFiles='); + print('E113 => getfetchUploadEnquiryFiles=> ${response['data']}'); + setState(() { + getUploadedData = List>.from(response['data']); + originalData = getUploadedData; + filteredData = List.from(originalData); + print('originalData - $originalData'); + }); + } else { + getUploadedData = []; + originalData = []; + } + } catch (e) { + print('Exception occurred: $e'); + } finally { + setState(() { + isLoading = false; + }); + } + } + + Future handleSave() async { + final request = http.MultipartRequest( + 'POST', + Uri.parse('${Env.apiUrl}enquiry/uploadEnquiryFiles'), + ); + + if (_token == null) { + throw Exception('Token not found. Please log in.'); + } + request.headers['Authorization'] = 'Bearer $_token'; + request.headers['app-signature'] = Env.App_Signature; + if (MultiFileUploadService().files.isEmpty) { + setState(() { + MultiFileUploadWidget.hasFiles = false; + }); + ToastHelper.showErrorToast( + context, + 'Please upload at least one document', + ); + return; + } + + // final id = int.tryParse(enqId.toString()) ?? 0; + + // get uploaded files + final uploadedFiles = MultiFileUploadService().files; + print("📁 Total uploaded files: ${uploadedFiles.length}"); + print( + "📂 File list (original names): ${uploadedFiles.map((f) => f.file.name).toList()}", + ); + + // Validate all labels present (optional but recommended) + for (final uf in uploadedFiles) { + print( + "📝 Checking label for file: ${uf.file.name}, current label: '${uf.label}'", + ); + if ((uf.label ?? '').trim().isEmpty) { + ToastHelper.showErrorToast( + context, + 'Please enter name for all uploaded documents', + ); + setState(() => isLoading = false); + return; + } + } + + // Add files + for (var uf in uploadedFiles) { + final pf = uf.file; + if (pf.bytes != null) { + request.files.add( + http.MultipartFile.fromBytes('files[]', pf.bytes!, filename: pf.name), + ); + } + } + + // ✅ Combine all names into a JSON array string + final claimDocNames = uploadedFiles.map((uf) => uf.label.trim()).toList(); + final encodedNames = jsonEncode(claimDocNames); + request.fields['files_doc_names'] = encodedNames; + request.fields['enquiry_id'] = widget.enqId!; + // ✅ Debug print + print("Payload being sent:"); + print("Files: ${uploadedFiles.map((f) => f.file.name).toList()}"); + print("Names (JSON): $encodedNames"); + + final response = await request.send(); + final responseBody = await response.stream.bytesToString(); + final decoded = jsonDecode(responseBody); + if (decoded['status'] == 'success') { + ToastHelper.showSuccessToast(context, 'File Uploaded'); + setState(() { + isLoading = false; + }); + print('Form data submitted successfully'); + fileService.clearAll(); + getList(); + } else { + ToastHelper.showErrorToast(context, "Failed: ${decoded['message']}"); + } + } + + Future handleDelete(id) async { + setState(() { + isLoading = true; + }); + + final path = 'enquiry/deleteEnquiryFile?file_id=$id'; + print('path - $path'); + try { + final response = await apiService.delete(id, path); + + print("response"); + print("response - $response"); + print("response - ${response['status']}"); + + if (response['status'] == 'success') { + ToastHelper.showSuccessToast(context, response['message']); + getList(); + } else { + ToastHelper.showSuccessToast(context, response['message']); + } + } catch (e) { + print('Exception occurred: $e'); + } finally { + setState(() { + isLoading = false; + }); + } + } + + @override + Widget build(BuildContext context) { + return Container( + color: Color(0xFFFFFFFF), + + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('File Upload', style: GoogleFonts.poppins(fontSize: 11)), + SizedBox(height: 3), + MultiFileUploadWidget(), + + Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + GestureDetector( + onTap: () { + handleSave(); + }, + child: Container( + padding: EdgeInsets.symmetric(horizontal: 6.0, vertical: 2), + + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(8.0), + color: Color(0xFF2E7D6E), + // color: Color(0xFF425B5B), + ), + child: Text( + 'Save', + style: GoogleFonts.poppins( + color: Colors.white, + fontSize: 11, + ), + ), + ), + ), + ], + ), + + SizedBox(height: 5), + Column( + children: List.generate(originalData.length, (index) { + final item = originalData[index]; + final fileName = item['file_name'] ?? ''; + final fileId = item['id'] ?? ''; + + return Container( + // margin: const EdgeInsets.only(bottom: 6), + padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 6), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(2), + border: Border( + bottom: BorderSide( + width: 0.2, + color: Colors.blueGrey.shade100, + ), + ), + ), + child: Row( + children: [ + Icon( + Icons.file_present_outlined, + size: 16, + color: Colors.grey[700], + ), + const SizedBox(width: 8), + Expanded( + child: Text( + fileName, + style: GoogleFonts.poppins(fontSize: 11), + overflow: TextOverflow.ellipsis, + ), + ), + + GestureDetector( + onTap: () { + final path = + 'api/enquiry/downloadEnquiryFiles?file_id=$fileId'; + print("Uploadpath $path"); + apiService.getPdfDownload(path, fileId); + }, + child: Icon(Icons.download, size: 16, color: Colors.blue), + ), + + // DELETE BUTTON (optional) + GestureDetector( + onTap: () { + handleDelete(fileId); + }, + child: Icon(Icons.delete, size: 16, color: Colors.red), + ), + ], + ), + ); + }), + ), + ], + ), + ); + } +} diff --git a/lib/presentation/screens/staff/Enquiry/raise_enq_form.dart b/lib/presentation/screens/staff/Enquiry/raise_enq_form.dart index d0c7c95..eec960d 100644 --- a/lib/presentation/screens/staff/Enquiry/raise_enq_form.dart +++ b/lib/presentation/screens/staff/Enquiry/raise_enq_form.dart @@ -483,6 +483,11 @@ class RaiseEnqFormState extends ConsumerState { label: "Partner Name", ).copyWith( filled: true, + // errorStyle: const TextStyle( + // fontSize: 9, + // + // // height: 0.0 + // ), fillColor: Colors.white, // 👈 makes the dropdown input white isDense: true, diff --git a/lib/presentation/screens/staff/Enquiry/tabs/createQuotation.dart b/lib/presentation/screens/staff/Enquiry/tabs/createQuotation.dart index 51096d9..6be7e41 100644 --- a/lib/presentation/screens/staff/Enquiry/tabs/createQuotation.dart +++ b/lib/presentation/screens/staff/Enquiry/tabs/createQuotation.dart @@ -503,38 +503,6 @@ class CreateProposalFormState extends ConsumerState { height: MediaQuery.of(context).size.height * 0.525, child: buildFormFields(context), ), - - // Row( - // crossAxisAlignment: CrossAxisAlignment.end, - // mainAxisAlignment: MainAxisAlignment.end, - // children: [ - // GestureDetector( - // onTap: () { - // handleDone(); - // // widget.onSubmit(controller.text.trim()); - // // Navigator.of(context).pop(); - // }, - // child: Container( - // padding: const EdgeInsets.symmetric( - // horizontal: 25.0, - // vertical: 10, - // ), - // decoration: BoxDecoration( - // borderRadius: BorderRadius.circular(8.0), - // color: const Color(0xFF425B5B), - // ), - // child: Text( - // 'Save', - // style: GoogleFonts.poppins( - // color: Colors.white, - // fontSize: 12, - // fontWeight: FontWeight.w600, - // ), - // ), - // ), - // ), - // ], - // ), ], ); } diff --git a/lib/presentation/themes/indicators/customizd_file_upload.dart b/lib/presentation/themes/indicators/customizd_file_upload.dart index 478cc8f..d88bd4a 100644 --- a/lib/presentation/themes/indicators/customizd_file_upload.dart +++ b/lib/presentation/themes/indicators/customizd_file_upload.dart @@ -8,6 +8,8 @@ class ThemedUploadField extends StatefulWidget { final String? hintText; final double? txtwidth; final double? txtheight; + final double? fontSZ; + final double? borderCirculr; final Color? backgroundColor; final Color? borderColor; final Color? errorBorderColor; @@ -23,6 +25,8 @@ class ThemedUploadField extends StatefulWidget { super.key, this.hintText, this.txtwidth, + this.borderCirculr, + this.fontSZ, this.txtheight, this.backgroundColor, this.borderColor, @@ -89,7 +93,9 @@ class _ThemedUploadFieldState extends State { ), decoration: BoxDecoration( color: widget.backgroundColor ?? Colors.white, - borderRadius: BorderRadius.circular(10), + borderRadius: BorderRadius.circular( + widget.borderCirculr ?? 10, + ), border: Border.all( color: errorMessage != null ? (widget.errorBorderColor ?? Colors.red) @@ -108,7 +114,7 @@ class _ThemedUploadFieldState extends State { widget.hintText ?? "Upload Document", style: TextStyle( - fontSize: 12, + fontSize: widget.fontSZ ?? 12, color: errorMessage != null ? Colors.red : Colors.black, @@ -119,6 +125,7 @@ class _ThemedUploadFieldState extends State { const Icon( Icons.file_upload_outlined, color: Colors.black, + size: 12, ), ], diff --git a/lib/presentation/themes/indicators/customizd_multi_file_upload.dart b/lib/presentation/themes/indicators/customizd_multi_file_upload.dart index 6d16a1c..e8533c1 100644 --- a/lib/presentation/themes/indicators/customizd_multi_file_upload.dart +++ b/lib/presentation/themes/indicators/customizd_multi_file_upload.dart @@ -51,7 +51,7 @@ class _ThemedMultiFileUploadFieldState Future _pickFile() async { final error = await fileService.pickFiles( maxFileSizeInMB: 3, - allowedExtensions: widget.allowedExtensions, + // allowedExtensions: widget.allowedExtensions, ); if (error != null) { @@ -60,6 +60,9 @@ class _ThemedMultiFileUploadFieldState context, ).showSnackBar(SnackBar(content: Text(error))); } else if (fileService.pickFiles != null) { + print('Files Picked '); + print('Files Picked fileService - ${fileService.pickFiles}'); + // setState(() { // selectedFileName = fileService.pickFiles!.name; // errorMessage = null; diff --git a/lib/presentation/themes/indicators/multi_fileUploadService.dart b/lib/presentation/themes/indicators/multi_fileUploadService.dart index 93e3841..7e0dc93 100644 --- a/lib/presentation/themes/indicators/multi_fileUploadService.dart +++ b/lib/presentation/themes/indicators/multi_fileUploadService.dart @@ -1,51 +1,71 @@ import 'package:file_picker/file_picker.dart'; +import 'package:flutter/widgets.dart'; + +class UploadedFile { + final PlatformFile file; + final TextEditingController controller; + UploadedFile({required this.file}) + : controller = TextEditingController( + text: file.extension != null + ? file.name.replaceAll('.${file.extension}', '') + : file.name, + ); + + String get label => controller.text; + void dispose() => controller.dispose(); +} class MultiFileUploadService { MultiFileUploadService._(); static final MultiFileUploadService _instance = MultiFileUploadService._(); factory MultiFileUploadService() => _instance; - final List _files = []; + final List _files = []; - List get files => List.unmodifiable(_files); + // Expose as unmodifiable but containing UploadedFile objects + List get files => List.unmodifiable(_files); // Allowed extensions - // static const allowedExtensions = ['pdf', 'png', 'jpg', 'jpeg', 'heic']; - static const defaultAllowedExtensions = ['pdf', 'png', 'jpg', 'jpeg']; + static const allowedExtensions = ['pdf', 'png', 'jpg', 'jpeg', 'heic']; - Future pickFiles({ - int maxFileSizeInMB = 3, - List? allowedExtensions, - }) async { - final extensions = allowedExtensions ?? defaultAllowedExtensions; + Future pickFiles({int maxFileSizeInMB = 3}) async { final result = await FilePicker.platform.pickFiles( allowMultiple: true, withData: true, type: FileType.custom, - allowedExtensions: extensions, + allowedExtensions: allowedExtensions, ); if (result != null) { - for (final file in result.files) { - final ext = file.extension?.toLowerCase() ?? ''; - final sizeInMB = (file.size / (1024 * 1024)); + for (final pf in result.files) { + final ext = pf.extension?.toLowerCase() ?? ''; + final sizeInMB = (pf.size / (1024 * 1024)); - if (!allowedExtensions!.contains(ext)) { - return "Unsupported file format: ${file.name}"; + if (!allowedExtensions.contains(ext)) { + return "Unsupported file format: ${pf.name}"; } if (sizeInMB > maxFileSizeInMB) { - return "File too large (${file.name}). Max $maxFileSizeInMB MB allowed."; + return "File too large (${pf.name}). Max $maxFileSizeInMB MB allowed."; } - } - _files.addAll(result.files); + // Wrap PlatformFile in our UploadedFile model + _files.add(UploadedFile(file: pf)); + } } - return null; // success + return null; } void removeFileAt(int index) { - if (index >= 0 && index < _files.length) _files.removeAt(index); + if (index >= 0 && index < _files.length) { + _files[index].dispose(); + _files.removeAt(index); + } } - void clearAll() => _files.clear(); + void clearAll() { + for (final f in _files) { + f.dispose(); + } + _files.clear(); + } } diff --git a/lib/presentation/themes/indicators/mutli_FileUpload.dart b/lib/presentation/themes/indicators/mutli_FileUpload.dart index dd9e841..6cee370 100644 --- a/lib/presentation/themes/indicators/mutli_FileUpload.dart +++ b/lib/presentation/themes/indicators/mutli_FileUpload.dart @@ -1,4 +1,7 @@ import 'package:flutter/material.dart'; +import 'package:google_fonts/google_fonts.dart'; + +import '../../layouts/responsive_layout.dart'; import 'multi_fileUploadService.dart'; class MultiFileUploadWidget extends StatefulWidget { @@ -6,6 +9,8 @@ class MultiFileUploadWidget extends StatefulWidget { @override State createState() => _MultiFileUploadWidgetState(); + + static bool hasFiles = false; // ✅ Static variable to check from parent } class _MultiFileUploadWidgetState extends State { @@ -40,13 +45,16 @@ class _MultiFileUploadWidgetState extends State { } else { setState(() { errorMessage = null; + MultiFileUploadWidget.hasFiles = fileService.files.isNotEmpty; }); } } void _removeFile(int index) { fileService.removeFileAt(index); - setState(() {}); + setState(() { + MultiFileUploadWidget.hasFiles = fileService.files.isNotEmpty; + }); } @override @@ -56,44 +64,117 @@ class _MultiFileUploadWidgetState extends State { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ + // if (ResponsiveLayout.isMobile(context)) ...[ OutlinedButton.icon( onPressed: _pickFiles, icon: const Icon( Icons.file_upload_outlined, - color: Color(0xFFE26728), - size: 24, + color: Color(0xFF2E7D6E), + size: 18, ), label: const Text( "Upload Documents", - style: TextStyle(fontSize: 14, color: Colors.black), + style: TextStyle(fontSize: 11, color: Colors.black), overflow: TextOverflow.ellipsis, ), style: OutlinedButton.styleFrom( - side: const BorderSide(color: Color(0xFFE26728)), + side: const BorderSide(color: Color(0xFF2E7D6E)), ), ), + const SizedBox(height: 3), + Text( + "Supports only PDF, PNG, JPG, JPEG,HEIC formats (max 10 MB each)", + style: GoogleFonts.poppins(fontSize: 8, color: Colors.grey), + ), + // ] + // else ...[ + // Row( + // children: [ + // OutlinedButton.icon( + // onPressed: _pickFiles, + // icon: const Icon( + // Icons.file_upload_outlined, + // color: Color(0xFFE26728), + // size: 24, + // ), + // label: const Text( + // "Upload Documents", + // style: TextStyle(fontSize: 14, color: Colors.black), + // overflow: TextOverflow.ellipsis, + // ), + // style: OutlinedButton.styleFrom( + // side: const BorderSide(color: Color(0xFFE26728)), + // ), + // ), + // const SizedBox(width: 12), + // // const Expanded( + // // child: Text( + // // "Supports only PDF, PNG, JPG, JPEG,HEIC formats (max 10 MB each)", + // // style: TextStyle(fontSize: 12, color: Colors.grey), + // // overflow: TextOverflow.ellipsis, + // // ), + // // ), + // ], + // ), + // ], + // if (fileService.files.isEmpty && errorMessage == null) ...[ + // const SizedBox(height: 4), + // const Text( + // "Required", + // style: TextStyle(color: Colors.red, fontSize: 12), + // ), + // ], if (errorMessage != null) ...[ const SizedBox(height: 4), Text( errorMessage!, - style: const TextStyle(color: Colors.red, fontSize: 12), + style: GoogleFonts.poppins(color: Colors.red, fontSize: 12), ), ], const SizedBox(height: 8), - ...files.asMap().entries.map((entry) { - final index = entry.key; - final file = entry.value; - return ListTile( - dense: true, - contentPadding: EdgeInsets.zero, - title: Text(file.name, style: const TextStyle(fontSize: 14)), - trailing: IconButton( - icon: const Icon(Icons.close, color: Colors.red), - onPressed: () => _removeFile(index), - ), + ...fileService.files.asMap().entries.map((entry) { + final index = entry.key; + final uploaded = entry.value; // UploadedFile + + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + ListTile( + dense: true, + contentPadding: EdgeInsets.zero, + title: Text( + uploaded.file.name, + style: GoogleFonts.poppins(fontSize: 11), + ), + trailing: IconButton( + padding: EdgeInsets.zero, + splashRadius: 2, + icon: const Icon(Icons.close, color: Colors.red, size: 14), + onPressed: () => _removeFile(index), + ), + ), + // Padding( + // padding: const EdgeInsets.only( + // left: 8.0, + // bottom: 8.0, + // right: 8.0, + // ), + // child: TextField( + // controller: uploaded.controller, + // style: GoogleFonts.poppins(fontSize: 11), + // decoration: InputDecoration( + // labelText: 'Enter document name', + // labelStyle: GoogleFonts.poppins(fontSize: 11), + // + // border: const OutlineInputBorder(), + // isDense: true, + // ), + // ), + // ), + ], ); }), ], diff --git a/lib/presentation/themes/indicators/side_Drawer_Panel.dart b/lib/presentation/themes/indicators/side_Drawer_Panel.dart new file mode 100644 index 0000000..ed26658 --- /dev/null +++ b/lib/presentation/themes/indicators/side_Drawer_Panel.dart @@ -0,0 +1,121 @@ +import 'package:flutter/material.dart'; +import 'package:google_fonts/google_fonts.dart'; + +/// A reusable side drawer that slides in from the right +/// Takes up 1/3 of the screen width +class SideDrawerPanel extends StatelessWidget { + final Widget child; + final String? title; + final VoidCallback? onClose; + final double? width; + + const SideDrawerPanel({ + Key? key, + required this.child, + this.title, + this.onClose, + this.width, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + final screenWidth = MediaQuery.of(context).size.width; + final drawerWidth = width ?? screenWidth / 2; + + return Container( + width: drawerWidth, + height: MediaQuery.of(context).size.height, + decoration: BoxDecoration( + color: Colors.white, + boxShadow: [ + BoxShadow( + color: Colors.black.withOpacity(0.1), + blurRadius: 10, + spreadRadius: 2, + offset: const Offset(-2, 0), + ), + ], + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // Header + Container( + padding: const EdgeInsets.all(8), + decoration: BoxDecoration( + color: const Color(0xFFFFFFFF), + // color: const Color(0xFFF8F9FF), + border: Border(bottom: BorderSide(color: Colors.grey.shade200)), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + if (title != null) + Text( + 'Proposal Creation', + style: GoogleFonts.poppins( + fontSize: 14, + fontWeight: FontWeight.w600, + color: const Color(0xFF1E293B), + ), + ), + const Spacer(), + IconButton( + icon: const Icon(Icons.close), + onPressed: onClose ?? () => Navigator.of(context).pop(), + color: Colors.grey.shade600, + tooltip: 'Close', + ), + ], + ), + ), + + // Content + Expanded( + child: SingleChildScrollView( + padding: const EdgeInsets.all(8), + // child: child, + child: Material( + color: Colors.white, + elevation: 0, + child: child, // Your CreateProposal_Quick() + ), + ), + ), + ], + ), + ); + } + + /// Show the drawer as a modal overlay + static Future show({ + required BuildContext context, + required Widget child, + String? title, + double? width, + bool barrierDismissible = true, + }) { + return showGeneralDialog( + context: context, + barrierDismissible: barrierDismissible, + barrierLabel: 'Drawer', + barrierColor: Colors.black.withOpacity(0.5), + transitionDuration: const Duration(milliseconds: 300), + pageBuilder: (context, animation, secondaryAnimation) { + return Align( + alignment: Alignment.centerRight, + child: SideDrawerPanel(title: title, width: width, child: child), + ); + }, + transitionBuilder: (context, animation, secondaryAnimation, child) { + return SlideTransition( + position: Tween(begin: const Offset(1, 0), end: Offset.zero) + .animate( + CurvedAnimation(parent: animation, curve: Curves.easeInOut), + ), + child: child, + ); + }, + ); + } +} diff --git a/lib/presentation/themes/indicators/text_field_theme.dart b/lib/presentation/themes/indicators/text_field_theme.dart index 01550d1..97947a0 100644 --- a/lib/presentation/themes/indicators/text_field_theme.dart +++ b/lib/presentation/themes/indicators/text_field_theme.dart @@ -20,6 +20,7 @@ class ThemedFormField extends HookWidget { this.backgroundColor, this.highlightColor, this.borderColor, + this.bordedRad, this.errorBorderColor, this.errorTextColor, this.readOnly = false, // ✅ new @@ -37,6 +38,7 @@ class ThemedFormField extends HookWidget { final double? verticalPad; final double? horizonalPad; + final double? bordedRad; final double? enableBorderWidth; final String? hintText; final Color? textColr; @@ -116,7 +118,7 @@ class ThemedFormField extends HookWidget { horizontal: 10, ), border: OutlineInputBorder( - borderRadius: BorderRadius.circular(10), + borderRadius: BorderRadius.circular(bordedRad ?? 10), // borderSide: BorderSide(color: Color(0xFF50A398)), borderSide: BorderSide( color: borderColor ?? Color(0xFFFFFFFF), @@ -124,7 +126,7 @@ class ThemedFormField extends HookWidget { ), ), enabledBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(10), + borderRadius: BorderRadius.circular(bordedRad ?? 10), // borderSide: BorderSide(color: Color(0xFF50A398)), borderSide: BorderSide( color: borderColor ?? backgroundColor ?? Color(0xFFFFFFFF),