diff --git a/.flutter-plugins-dependencies b/.flutter-plugins-dependencies index e80da35..da172c1 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":"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":"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":"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":"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-09-18 18:22:04.874824","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":"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":"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":"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":"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-09-27 12:50:32.972743","version":"3.32.1","swift_package_manager_enabled":{"ios":false,"macos":false}} \ No newline at end of file diff --git a/assets/miscellaneous/Edit.png b/assets/miscellaneous/Edit.png new file mode 100644 index 0000000..bae88a6 Binary files /dev/null and b/assets/miscellaneous/Edit.png differ diff --git a/assets/miscellaneous/Edit_muted.png b/assets/miscellaneous/Edit_muted.png new file mode 100644 index 0000000..eeec474 Binary files /dev/null and b/assets/miscellaneous/Edit_muted.png differ diff --git a/assets/miscellaneous/image_1.png b/assets/miscellaneous/image_1.png new file mode 100644 index 0000000..f2f8ab0 Binary files /dev/null and b/assets/miscellaneous/image_1.png differ diff --git a/assets/miscellaneous/image_2.png b/assets/miscellaneous/image_2.png new file mode 100644 index 0000000..cd735fc Binary files /dev/null and b/assets/miscellaneous/image_2.png differ diff --git a/assets/miscellaneous/image_3.png b/assets/miscellaneous/image_3.png new file mode 100644 index 0000000..975e8be Binary files /dev/null and b/assets/miscellaneous/image_3.png differ diff --git a/assets/miscellaneous/success_img1.png b/assets/miscellaneous/success_img1.png new file mode 100644 index 0000000..0038197 Binary files /dev/null and b/assets/miscellaneous/success_img1.png differ diff --git a/lib/core/routing/app_router.dart b/lib/core/routing/app_router.dart index 7431719..69fb3f0 100644 --- a/lib/core/routing/app_router.dart +++ b/lib/core/routing/app_router.dart @@ -18,6 +18,7 @@ import '../../presentation/screens/UserManagement/Staff/staffList.dart'; import '../../presentation/screens/home/home_screen.dart'; import '../../presentation/screens/login/login_screen.dart'; import '../../presentation/screens/splash/splash_screen.dart'; +import '../../presentation/screens/staff/Enquiry/enquiry_list.dart'; import '../../presentation/screens/staff/policy/policy.dart'; import '../../presentation/screens/staff/policy/policy_list.dart'; import '../../presentation/screens/staff/quotations/quotation.dart'; @@ -145,6 +146,15 @@ final GoRouter appRouter = GoRouter( path: AppRoutes.policy, builder: (context, state) => const PolicyScreen(), ), + GoRoute( + path: AppRoutes.policy, + builder: (context, state) => const PolicyScreen(), + ), + + GoRoute( + path: AppRoutes.enquiryForStaff, + builder: (context, state) => const EnquiryStaff(), + ), ], redirect: (context, state) async { final loggedIn = await AuthService.isLoggedIn(); diff --git a/lib/core/routing/routes.dart b/lib/core/routing/routes.dart index 2656487..dea9dc1 100644 --- a/lib/core/routing/routes.dart +++ b/lib/core/routing/routes.dart @@ -9,6 +9,7 @@ class AppRoutes { static const String staffLst = '/staffLst'; static const staff = '/staff/:id'; static const String enquiryLst = '/enquiryLst'; + static const String enquiryForStaff = '/enquiryForStaff'; static const String tabEnquiry = '/tabEnquiry'; static const String claimlist = '/claimList'; diff --git a/lib/core/services/api_service.dart b/lib/core/services/api_service.dart index 1c5e8da..ccae27a 100644 --- a/lib/core/services/api_service.dart +++ b/lib/core/services/api_service.dart @@ -156,7 +156,14 @@ class ApiService { Future getPdfDownload(String path, String id) async { print('getPdfDownload 1'); + print('getPdfDownload 1 - $path'); + + if (path.isEmpty) { + ToastHelper.showInfoToast(context, 'No file path found.'); + return; + } final url = Uri.parse('https://venbait.in/nhance/partner/dev/$path'); + await _initializeToken(); print('getPdfDownload 2'); if (_token == null) throw Exception('Token not found. Please log in.'); @@ -205,7 +212,10 @@ class ApiService { ], ), ); + } else if (response.statusCode == 500) { + ToastHelper.showInfoToast(context, 'No File Not Found'); } else { + ToastHelper.showInfoToast(context, 'No File Not Found'); throw Exception('Failed to download file'); } } @@ -279,6 +289,34 @@ class ApiService { return response; } + // ----------------------------------- Dashboard ------------------------------------------------- + Future> fetchDashboard(int id, role) async { + // print(_token); + if (_token == null) { + await _initializeToken(); + } + + final url; + + if (role == 'manager') { + url = Uri.parse('${Env.apiUrl}dashboard/managerDashboard?manager_id=$id'); + } else if (role == 'staff') { + url = Uri.parse('${Env.apiUrl}dashboard/staffDashboard?staff_id=$id'); + } else { + url = Uri.parse('${Env.apiUrl}dashboard/agentDashboard?agent_id=$id'); + } + // final url = Uri.parse( + // 'https://venbait.in/nhance/partner/dev/api/agent/agentList?manager_id=${managerId}', + // ); + final headers = { + 'Authorization': 'Bearer $_token' ?? '', + 'App-Signature': 'nhance-partner-2025-signature-35468846JRhH551HK', + }; + + final response = await _makeGetRequest(url, headers); + return response; + } + // -------------------------------- AGENT ---------------------------------------------- Future> fetchAgentIncentiveList(agentId) async { @@ -544,6 +582,38 @@ class ApiService { return response; } + Future> fetchPolicyDataOnlyList(int id, role) async { + // print(_token); + if (_token == null) { + await _initializeToken(); + } + + final userId = '1'; + final url; + + if (role == 'manager') { + url = Uri.parse( + '${Env.apiUrl}enquiry/enquiryList?manager_id=$id&only_policy_data=true', + ); + } else { + url = Uri.parse( + '${Env.apiUrl}enquiry/enquiryList?staff_id=$id&only_policy_data=true', + ); + } + // else { + // url = Uri.parse('${Env.apiUrl}endorsement/endorsementList?agent_id=$id'); + // } + // final url = Uri.parse( + // 'https://venbait.in/nhance/partner/dev/api/agent/agentList?manager_id=${managerId}', + // ); + final headers = { + 'Authorization': 'Bearer $_token' ?? '', + 'App-Signature': 'nhance-partner-2025-signature-35468846JRhH551HK', + }; + final response = await _makeGetRequest(url, headers); + return response; + } + // ----------------------------------- CLAIMS ------------------------------------------------- Future> fetchClaimList(int id, role) async { diff --git a/lib/data/utils/validators.dart b/lib/data/utils/validators.dart index 48d373d..2a827a5 100644 --- a/lib/data/utils/validators.dart +++ b/lib/data/utils/validators.dart @@ -76,6 +76,17 @@ class Validators { return null; } + static String? doubleNumber(String? value, String label) { + if (value == null || value.trim().isEmpty) { + return "$label is required"; + } + // allow integers or decimals with optional fraction part + if (!RegExp(r'^[0-9]+(\.[0-9]+)?$').hasMatch(value.trim())) { + return "$label must be a valid number"; + } + return null; + } + static String? phone(String? value, String label) { if (value == null || value.trim().isEmpty) { return "Required"; diff --git a/lib/presentation/layouts/main_layout.dart b/lib/presentation/layouts/main_layout.dart index fbbfac2..50fe643 100644 --- a/lib/presentation/layouts/main_layout.dart +++ b/lib/presentation/layouts/main_layout.dart @@ -36,18 +36,18 @@ class MainLayout extends StatelessWidget { ), drawer: const DrawerMenu(), body: body, - bottomNavigationBar: MobileTabs( - onTabChanged: (index) { - debugPrint("Mobile tab changed to $index"); - if (index == 0) { - context.go(AppRoutes.dashboard); - } else if (index == 1) { - context.go(AppRoutes.profile); - } else if (index == 2) { - // ref.read(enquiryIdProvider.notifier).state = null; - context.go(AppRoutes.enquiryLst); - } - }, + bottomNavigationBar: MobileBottomMenu( + // onTabChanged: (index) { + // debugPrint("Mobile tab changed to $index"); + // if (index == 0) { + // context.go(AppRoutes.dashboard); + // } else if (index == 1) { + // context.go(AppRoutes.profile); + // } else if (index == 2) { + // // ref.read(enquiryIdProvider.notifier).state = null; + // context.go(AppRoutes.enquiryLst); + // } + // }, ), ), diff --git a/lib/presentation/providers/quotation_staff_proivder.dart b/lib/presentation/providers/quotation_staff_proivder.dart new file mode 100644 index 0000000..89c33fa --- /dev/null +++ b/lib/presentation/providers/quotation_staff_proivder.dart @@ -0,0 +1,3 @@ +import 'package:flutter_riverpod/flutter_riverpod.dart'; + +final quotationStaffIdProvider = StateProvider((ref) => null); diff --git a/lib/presentation/screens/Enquiry/enquiry/enquiry_tab.dart b/lib/presentation/screens/Enquiry/enquiry/enquiry_tab.dart index 1cc497b..a9cfd0d 100644 --- a/lib/presentation/screens/Enquiry/enquiry/enquiry_tab.dart +++ b/lib/presentation/screens/Enquiry/enquiry/enquiry_tab.dart @@ -140,6 +140,15 @@ class EnquiryTabState extends ConsumerState { print("APISERTOKEN - $_token"); } + @override + void dispose() { + // Dispose all TextEditingControllers + for (var controller in controllers.values) { + controller.dispose(); + } + super.dispose(); + } + Future getVehicleType() async { print('getClaimList called'); setState(() { diff --git a/lib/presentation/screens/Enquiry/enquiry/policy_popup.dart b/lib/presentation/screens/Enquiry/enquiry/policy_popup.dart index cceede1..3eb565e 100644 --- a/lib/presentation/screens/Enquiry/enquiry/policy_popup.dart +++ b/lib/presentation/screens/Enquiry/enquiry/policy_popup.dart @@ -102,6 +102,15 @@ class _AddDialogState extends State { print("APISERTOKEN - $_token"); } + @override + void dispose() { + // Dispose all TextEditingControllers + for (var controller in controllers.values) { + controller.dispose(); + } + super.dispose(); + } + Future getClaimsType() async { print('getClaimList called'); setState(() { @@ -244,12 +253,6 @@ class _AddDialogState extends State { } } - @override - void dispose() { - // controllers.dispose(); - super.dispose(); - } - @override Widget build(BuildContext context) { return AlertDialog( diff --git a/lib/presentation/screens/Enquiry/enquiry/policy_tab.dart b/lib/presentation/screens/Enquiry/enquiry/policy_tab.dart index db40517..3f156f7 100644 --- a/lib/presentation/screens/Enquiry/enquiry/policy_tab.dart +++ b/lib/presentation/screens/Enquiry/enquiry/policy_tab.dart @@ -81,6 +81,15 @@ class PolicyTabState extends ConsumerState { }); } + @override + void dispose() { + // Dispose all TextEditingControllers + for (var controller in controllers.values) { + controller.dispose(); + } + super.dispose(); + } + Future _initializeToken() async { _token = await AuthService.getToken(); print("APISERTOKEN - $_token"); diff --git a/lib/presentation/screens/Enquiry/enquiry/quotation_tab.dart b/lib/presentation/screens/Enquiry/enquiry/quotation_tab.dart index 7be52a0..356a2fa 100644 --- a/lib/presentation/screens/Enquiry/enquiry/quotation_tab.dart +++ b/lib/presentation/screens/Enquiry/enquiry/quotation_tab.dart @@ -123,6 +123,15 @@ class QuotationTabState extends ConsumerState { getQuotationList(managerId); } + @override + void dispose() { + // Dispose all TextEditingControllers + for (var controller in controllers.values) { + controller.dispose(); + } + super.dispose(); + } + Future getQuotationList(managerId) async { print('getClaimList called MagId - $managerId'); setState(() { diff --git a/lib/presentation/screens/Enquiry/enquiryList.dart b/lib/presentation/screens/Enquiry/enquiryList.dart index 2e4038d..5d22865 100644 --- a/lib/presentation/screens/Enquiry/enquiryList.dart +++ b/lib/presentation/screens/Enquiry/enquiryList.dart @@ -106,7 +106,7 @@ class EnquiryListState extends ConsumerState { filteredData = getStaffData.where((item) { // final isActiveStatus = item['is_active'] == "1" ? "active" : "inactive"; - return (item['created_on'] ?? '').toLowerCase().contains( + return (_formatDate(item['created_on']) ?? '').toLowerCase().contains( query.toLowerCase(), ) || (item['reg_no'] ?? '').toLowerCase().contains( @@ -142,10 +142,12 @@ class EnquiryListState extends ConsumerState { print('EDITStaff - ${data['id']}'); // dynamic id = data['id']; // context.go('/tabEnquiry/$id'); - + setState(() { + final id = data['id'].toString(); + ref.read(enquiryIdProvider.notifier).state = id; + }); // update provider - final id = data['id'].toString(); - ref.read(enquiryIdProvider.notifier).state = id; + context.go(AppRoutes.tabEnquiry); }, child: Row( diff --git a/lib/presentation/screens/Enquiry/policy_claims_endros/claims.dart b/lib/presentation/screens/Enquiry/policy_claims_endros/claims.dart index 8836eb0..0e4be99 100644 --- a/lib/presentation/screens/Enquiry/policy_claims_endros/claims.dart +++ b/lib/presentation/screens/Enquiry/policy_claims_endros/claims.dart @@ -31,6 +31,8 @@ class claimListState extends ConsumerState { List> filteredData = []; bool isLoading = false; + final TextEditingController _searchStaffController = TextEditingController(); + @override void initState() { super.initState(); @@ -114,25 +116,23 @@ class claimListState extends ConsumerState { (item['policy_no'] ?? '').toLowerCase().contains( query.toLowerCase(), ) || - (item['policy_end_date'] ?? '').toLowerCase().contains( - query.toLowerCase(), - ) || - (item['claim_type'] ?? '').toLowerCase().contains( + (item['claim_type_value'] ?? '').toLowerCase().contains( query.toLowerCase(), ) || (item['claim_number'] ?? '').toLowerCase().contains( query.toLowerCase(), - ); + ) || + (_formatDate( + item['policy_end_date'], + ).toLowerCase()).contains(query.toLowerCase()); }).toList(); }); } - final TextEditingController _searchStaffController = TextEditingController(); - String _formatDate(String rawDate) { try { final dateTime = DateTime.parse(rawDate); - return DateFormat('dd/MM/yyyy HH:mm').format(dateTime); // 24-hour format + return DateFormat('dd/MM/yyyy').format(dateTime); // date only } catch (e) { return rawDate; // fallback if parsing fails } @@ -323,17 +323,17 @@ class claimListState extends ConsumerState { ), ), Expanded( - flex: 2, + flex: 3, child: Text('Claim Type', style: _headerStyle), ), Expanded( flex: 2, child: Text('Claim No.', style: _headerStyle), ), - Expanded( - flex: 1, - child: Text('Action', style: _headerStyle), - ), + // Expanded( + // flex: 1, + // child: Text('Action', style: _headerStyle), + // ), ], ), ), @@ -425,86 +425,47 @@ class claimListState extends ConsumerState { Expanded( flex: 2, child: Text( - item['reg_no'] != null - ? _formatDate(item['reg_no']) - : '-', // fallback + item['reg_no'] ?? '-', // fallback style: _dataBold, ), ), Expanded( flex: 2, child: Text( - item['insurer_name'] != null - ? _formatDate(item['insurer_name']) - : '-', // fallback + item['insurer_name'] ?? '-', // fallback + style: _dataBold, + ), + ), + Expanded( + flex: 2, + child: Text(item['policy_no'] ?? '-', style: _dataBold), + ), + Expanded( + flex: 2, + child: Text( + // _formatDate(item['policy_end_date']) ?? '-', + item['policy_end_date'] != null + ? _formatDate(item['policy_end_date']) + : '-', + style: _dataBold, + ), + ), + Expanded( + flex: 3, + child: Text( + item['claim_type_value'] ?? '-', // fallback style: _dataBold, ), ), Expanded( flex: 2, child: Text( - _formatDate(item['policy_no']) ?? '-', + item['claim_number'] ?? '-', // fallback style: _dataBold, ), ), - Expanded( - flex: 2, - child: Text( - _formatDate(item['policy_end_date']) ?? '-', - style: _dataBold, - ), - ), - Expanded( - flex: 2, - child: Text( - item['claim_type'] != null - ? _formatDate(item['claim_number']) - : '-', // fallback - style: _dataBold, - ), - ), - Expanded( - flex: 2, - child: Text( - item['claim_number'] != null - ? _formatDate(item['claim_number']) - : '-', // fallback - style: _dataBold, - ), - ), - Expanded( - flex: 1, - child: Row( - children: [ - PopupMenuButton( - color: Colors.white, - padding: EdgeInsets.zero, - offset: Offset(0, 30), - icon: Icon( - Icons.more_vert, - color: Color(0xFF475569), - size: 14, - ), - itemBuilder: (context) => [ - CustomPopupMenuEntry( - child: Container( - padding: EdgeInsets.symmetric( - horizontal: 8, - vertical: 8, - ), - child: Column( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.center, - children: _buildPopupMenuActions(context, item), - ), - ), - ), - ], - ), - ], - ), - ), - // Expanded(s + + // Expanded( // flex: 1, // child: Row( // children: [ @@ -561,24 +522,24 @@ class claimListState extends ConsumerState { item['reg_no'] != null ? _formatDate(item['reg_no']) : '-', style: _headerStyle, ), - PopupMenuButton( - color: Colors.white, - padding: EdgeInsets.zero, - offset: Offset(0, 30), - icon: Icon(Icons.more_vert, color: Color(0xFF475569), size: 14), - itemBuilder: (context) => [ - CustomPopupMenuEntry( - child: Container( - padding: EdgeInsets.symmetric(horizontal: 8, vertical: 8), - child: Column( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.center, - children: _buildPopupMenuActions(context, item), - ), - ), - ), - ], - ), + // PopupMenuButton( + // color: Colors.white, + // padding: EdgeInsets.zero, + // offset: Offset(0, 30), + // icon: Icon(Icons.more_vert, color: Color(0xFF475569), size: 14), + // itemBuilder: (context) => [ + // CustomPopupMenuEntry( + // child: Container( + // padding: EdgeInsets.symmetric(horizontal: 8, vertical: 8), + // child: Column( + // mainAxisSize: MainAxisSize.min, + // mainAxisAlignment: MainAxisAlignment.center, + // children: _buildPopupMenuActions(context, item), + // ), + // ), + // ), + // ], + // ), ], ), const Divider(color: Color(0xffD9EBE8), thickness: 0.8), @@ -597,8 +558,8 @@ class claimListState extends ConsumerState { //company const Text("Company", style: _cardheaderStyle), Text( - item['reg_no'] != null - ? _formatDate(item['reg_no']) + item['policy_end_date'] != null + ? _formatDate(item['policy_end_date']) : '-', style: _cardBodyStyle, maxLines: 3, @@ -616,7 +577,7 @@ class claimListState extends ConsumerState { //status const Text("Claim Type", style: _cardheaderStyle), Text( - item['claim_type'] ?? '-', + item['claim_type_value'] ?? '-', style: _cardBodyStyle, maxLines: 2, softWrap: true, @@ -638,10 +599,7 @@ class claimListState extends ConsumerState { children: [ //date and time const Text("policy Number", style: _cardheaderStyle), - Text( - _formatDate(item['policy_no']) ?? '-', - style: _cardBodyStyle, - ), + Text(item['policy_no'] ?? '-', style: _cardBodyStyle), ], ), ), diff --git a/lib/presentation/screens/Enquiry/policy_claims_endros/endrosment.dart b/lib/presentation/screens/Enquiry/policy_claims_endros/endrosment.dart index 20a5156..3921832 100644 --- a/lib/presentation/screens/Enquiry/policy_claims_endros/endrosment.dart +++ b/lib/presentation/screens/Enquiry/policy_claims_endros/endrosment.dart @@ -184,7 +184,7 @@ class endosementState extends ConsumerState { color: Color(0xFF425B5B), ), Text( - "Endosement", + "Endorsement", style: TextStyle( fontSize: 18, fontWeight: FontWeight.w600, @@ -325,10 +325,10 @@ class endosementState extends ConsumerState { style: _headerStyle, ), ), - Expanded( - flex: 1, - child: Text('Action', style: _headerStyle), - ), + // Expanded( + // flex: 1, + // child: Text('Action', style: _headerStyle), + // ), ], ), ), @@ -420,74 +420,65 @@ class endosementState extends ConsumerState { Expanded( flex: 2, child: Text( - item['reg_no'] != null - ? _formatDate(item['reg_no']) - : '-', // fallback + item['reg_no'] ?? '-', // fallback style: _dataBold, ), ), Expanded( flex: 2, child: Text( - item['insurer_name'] != null - ? _formatDate(item['insurer_name']) - : '-', // fallback + item['insurer_name'] ?? '-', // fallback style: _dataBold, ), ), + Expanded( + flex: 2, + child: Text(item['policy_number'] ?? '-', style: _dataBold), + ), + Expanded( flex: 2, child: Text( - _formatDate(item['policy_number']) ?? '-', + item['endorsement_type_value'] ?? '-', style: _dataBold, ), ), Expanded( flex: 2, - child: Text( - _formatDate(item['endorsement_no']) ?? '-', - style: _dataBold, - ), - ), - Expanded( - flex: 2, - child: Text( - _formatDate(item['endorsement_type']) ?? '-', - style: _dataBold, - ), - ), - Expanded( - flex: 1, - child: Row( - children: [ - PopupMenuButton( - color: Colors.white, - padding: EdgeInsets.zero, - offset: Offset(0, 30), - icon: Icon( - Icons.more_vert, - color: Color(0xFF475569), - size: 14, - ), - itemBuilder: (context) => [ - CustomPopupMenuEntry( - child: Container( - padding: EdgeInsets.symmetric( - horizontal: 8, - vertical: 8, - ), - child: Column( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.center, - children: _buildPopupMenuActions(context, item), - ), - ), - ), - ], - ), - ], - ), + child: Text(item['endorsement_no'] ?? '-', style: _dataBold), ), + // Expanded( + // flex: 1, + // child: Row( + // children: [ + // PopupMenuButton( + // color: Colors.white, + // padding: EdgeInsets.zero, + // offset: Offset(0, 30), + // icon: Icon( + // Icons.more_vert, + // color: Color(0xFF475569), + // size: 14, + // ), + // itemBuilder: (context) => [ + // CustomPopupMenuEntry( + // child: Container( + // padding: EdgeInsets.symmetric( + // horizontal: 8, + // vertical: 8, + // ), + // child: Column( + // mainAxisSize: MainAxisSize.min, + // mainAxisAlignment: MainAxisAlignment.center, + // children: _buildPopupMenuActions(context, item), + // ), + // ), + // ), + // ], + // ), + // ], + // ), + // ), ], ), ); @@ -509,28 +500,25 @@ class endosementState extends ConsumerState { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ //reg no - Text( - item['reg_no'] != null ? _formatDate(item['reg_no']) : '-', - style: _headerStyle, - ), - PopupMenuButton( - color: Colors.white, - padding: EdgeInsets.zero, - offset: Offset(0, 30), - icon: Icon(Icons.more_vert, color: Color(0xFF475569), size: 14), - itemBuilder: (context) => [ - CustomPopupMenuEntry( - child: Container( - padding: EdgeInsets.symmetric(horizontal: 8, vertical: 8), - child: Column( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.center, - children: _buildPopupMenuActions(context, item), - ), - ), - ), - ], - ), + Text(item['reg_no'] ?? '-', style: _headerStyle), + // PopupMenuButton( + // color: Colors.white, + // padding: EdgeInsets.zero, + // offset: Offset(0, 30), + // icon: Icon(Icons.more_vert, color: Color(0xFF475569), size: 14), + // itemBuilder: (context) => [ + // CustomPopupMenuEntry( + // child: Container( + // padding: EdgeInsets.symmetric(horizontal: 8, vertical: 8), + // child: Column( + // mainAxisSize: MainAxisSize.min, + // mainAxisAlignment: MainAxisAlignment.center, + // children: _buildPopupMenuActions(context, item), + // ), + // ), + // ), + // ], + // ), ], ), const Divider(color: Color(0xffD9EBE8), thickness: 0.8), @@ -549,9 +537,7 @@ class endosementState extends ConsumerState { //company const Text("Company", style: _cardheaderStyle), Text( - item['insurer_name'] != null - ? _formatDate(item['insurer_name']) - : '-', + item['insurer_name'] ?? '-', style: _cardBodyStyle, maxLines: 3, softWrap: true, @@ -568,7 +554,7 @@ class endosementState extends ConsumerState { //status const Text("Endorsement Type", style: _cardheaderStyle), Text( - item['endorsement_type'] ?? '-', + item['endorsement_type_value'] ?? '-', style: _cardBodyStyle, maxLines: 2, softWrap: true, diff --git a/lib/presentation/screens/UserManagement/Agent/agent.dart b/lib/presentation/screens/UserManagement/Agent/agent.dart index ee694e7..5201015 100644 --- a/lib/presentation/screens/UserManagement/Agent/agent.dart +++ b/lib/presentation/screens/UserManagement/Agent/agent.dart @@ -15,6 +15,7 @@ import '../../../../core/config/env.dart'; import '../../../../core/services/api_service.dart'; import '../../../../data/services/auth_service.dart'; import '../../../../data/utils/Pagination.dart'; +import '../../../../data/utils/toastNotification.dart'; import '../../../../data/utils/validators.dart'; import '../../../layouts/main_layout.dart'; import '../../../providers/manager_provider.dart'; @@ -237,6 +238,15 @@ class AgentState extends ConsumerState { print("✅ Agent submitted successfully!"); print("Response: ${response.body}"); + isUpdating + ? ToastHelper.showSuccessToast( + context, + 'Agent Updated Successfully', + ) + : ToastHelper.showSuccessToast( + context, + 'Agent Created Successfully', + ); context.go(AppRoutes.agentLst); } else { final responseBody = jsonDecode(response.body); diff --git a/lib/presentation/screens/UserManagement/Agent/agentIncentiveFile.dart b/lib/presentation/screens/UserManagement/Agent/agentIncentiveFile.dart index 504a14e..08e8d28 100644 --- a/lib/presentation/screens/UserManagement/Agent/agentIncentiveFile.dart +++ b/lib/presentation/screens/UserManagement/Agent/agentIncentiveFile.dart @@ -1,5 +1,6 @@ // import 'dart:io' as html; // import 'dart:nativewrappers/_internal/vm/lib/typed_data_patch.dart'; +import 'dart:convert'; import 'dart:typed_data'; // Import for Uint8List import 'package:dropdown_search/dropdown_search.dart'; import 'package:file_picker/file_picker.dart'; @@ -8,7 +9,9 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:http/http.dart' as http; import 'package:intl/intl.dart'; +import 'package:nhance_partner/data/utils/toastNotification.dart'; import 'package:nhance_partner/presentation/themes/indicators/text_field_theme.dart'; +import 'package:toastification/toastification.dart'; import 'package:universal_html/html.dart' as html; import '../../../../core/config/env.dart'; @@ -202,25 +205,31 @@ class UploadIncentiveModalState extends ConsumerState { } Future handleSave() async { - if (!_formKey.currentState!.validate()) return; - print("Handl1"); - final dataSet = dataDetails(); - print("dataSetAgent - $dataSet"); - print("Handl13f"); + if (agentId == null) { + ToastHelper.showErrorToast(context, 'Select Agent Name'); + } else { + if (!_formKey.currentState!.validate()) return; + print("Handl1"); + final dataSet = dataDetails(); + print("dataSetAgent - $dataSet"); + print("Handl13f"); - if (passportFile == null) { - print("❌ No file selected!"); - ScaffoldMessenger.of(context).showSnackBar( - const SnackBar(content: Text("Please select a file before submitting")), - ); - return; + if (passportFile == null) { + print("❌ No file selected!"); + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text("Please select a file before submitting"), + ), + ); + return; + } + + setState(() { + uploadIncentiveData(dataSet); + }); + + print("Handl2"); } - - setState(() { - uploadIncentiveData(dataSet); - }); - - print("Handl2"); } Future uploadIncentiveData(Map dataSet) async { @@ -303,6 +312,7 @@ class UploadIncentiveModalState extends ConsumerState { final response = await http.Response.fromStream(streamedResponse); print("Response status: ${response.statusCode}"); print("Response body: ${response.body}"); + final responseBody = json.decode(response.body); if (response.statusCode == 200 || response.statusCode == 201) { // dispose(); @@ -313,7 +323,19 @@ class UploadIncentiveModalState extends ConsumerState { final data = dataDetails(); final agentId = data["agent_id"]; - getAgentIncenctiveFileList(agentId); + final status = responseBody['status']; + final message = responseBody['data'] ?? ''; + + if (status == 'success') { + ToastHelper.showSuccessToast(context, 'File uploaded successfully!'); + getAgentIncenctiveFileList(agentId); + } else { + ToastHelper.showErrorToast( + context, + message.isNotEmpty ? message : 'Something went wrong!', + ); + } + // context.go(AppRoutes.agentLst); } else { print("❌ Submission failed. Status: ${response.statusCode}"); diff --git a/lib/presentation/screens/UserManagement/Agent/agentList.dart b/lib/presentation/screens/UserManagement/Agent/agentList.dart index 4bb0061..a106659 100644 --- a/lib/presentation/screens/UserManagement/Agent/agentList.dart +++ b/lib/presentation/screens/UserManagement/Agent/agentList.dart @@ -485,37 +485,68 @@ class AgentListState extends ConsumerState { ), Expanded( flex: 1, - child: Row( - children: [ - PopupMenuButton( - color: Colors.white, - padding: EdgeInsets.zero, - offset: Offset(0, 30), - icon: Icon( - Icons.more_vert, - color: Color(0xFF475569), - size: 14, - ), - itemBuilder: (context) => [ - if (item['is_active'] == "1") - CustomPopupMenuEntry( - child: Container( - padding: EdgeInsets.symmetric( - horizontal: 8, - vertical: 8, - ), - child: Column( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.center, - children: _buildPopupMenuActions(context, item), - ), + child: item['is_active'] == "1" + ? Row( + children: [ + GestureDetector( + onTap: () { + // Navigator.pop(context); + print('EDITStaff - ${item['id']}'); + dynamic id = item['id']; + context.go('/agent/$id'); + }, + child: Image.asset( + "assets/miscellaneous/Edit.png", + height: 15, + width: 15, ), ), - ], - ), - ], - ), + ], + ) + : Row( + children: [ + Image.asset( + "assets/miscellaneous/Edit_muted.png", + height: 15, + width: 15, + ), + ], + ), ), + + // Expanded( + // flex: 1, + // child: Row( + // children: [ + // PopupMenuButton( + // color: Colors.white, + // padding: EdgeInsets.zero, + // offset: Offset(0, 30), + // icon: Icon( + // Icons.more_vert, + // color: Color(0xFF475569), + // size: 14, + // ), + // itemBuilder: (context) => [ + // if (item['is_active'] == "1") + // CustomPopupMenuEntry( + // child: Container( + // padding: EdgeInsets.symmetric( + // horizontal: 8, + // vertical: 8, + // ), + // child: Column( + // mainAxisSize: MainAxisSize.min, + // mainAxisAlignment: MainAxisAlignment.center, + // children: _buildPopupMenuActions(context, item), + // ), + // ), + // ), + // ], + // ), + // ], + // ), + // ), ], ), ); diff --git a/lib/presentation/screens/UserManagement/Profile/profile_mobile.dart b/lib/presentation/screens/UserManagement/Profile/profile_mobile.dart index c427fd6..658a288 100644 --- a/lib/presentation/screens/UserManagement/Profile/profile_mobile.dart +++ b/lib/presentation/screens/UserManagement/Profile/profile_mobile.dart @@ -155,8 +155,22 @@ class ProfileState extends ConsumerState { crossAxisAlignment: CrossAxisAlignment.center, children: [ Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, + mainAxisAlignment: MainAxisAlignment.start, children: [ + if (!isProfile) ...[ + IconButton( + onPressed: () { + setState(() { + isProfile = true; + }); + }, + icon: Icon( + Icons.arrow_back_ios_new_outlined, + size: 16, + ), + ), + SizedBox(width: 4), + ], Text( isProfile ? '' : 'Incentive Files', style: _topheaderStyle, @@ -168,7 +182,7 @@ class ProfileState extends ConsumerState { ? Container( color: Colors.white, padding: ResponsiveLayout.isMobile(context) - ? EdgeInsets.all(16.0) + ? EdgeInsets.all(8.0) : null, child: isLoading ? const Center( @@ -176,10 +190,10 @@ class ProfileState extends ConsumerState { ) : Column( children: [ - const SizedBox(height: 25), + // const SizedBox(height: 25), Container( - width: 70, - height: 70, + width: 60, + height: 60, decoration: BoxDecoration( color: Color(0xFFD9D9D9), borderRadius: BorderRadius.circular( @@ -188,7 +202,7 @@ class ProfileState extends ConsumerState { ), child: Icon( Icons.person_outline, - size: 50, + size: 30, color: Colors.black, ), ), @@ -669,7 +683,7 @@ class ProfileState extends ConsumerState { static const _topheaderStyle = TextStyle( color: Colors.black, fontWeight: FontWeight.w600, - fontSize: 19, + fontSize: 14, ); } diff --git a/lib/presentation/screens/UserManagement/Staff/staff.dart b/lib/presentation/screens/UserManagement/Staff/staff.dart index 47cb703..aeb4157 100644 --- a/lib/presentation/screens/UserManagement/Staff/staff.dart +++ b/lib/presentation/screens/UserManagement/Staff/staff.dart @@ -7,6 +7,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:go_router/go_router.dart'; import 'package:http/http.dart' as http; import 'package:nhance_partner/core/routing/routes.dart'; +import 'package:nhance_partner/data/utils/toastNotification.dart'; import 'package:nhance_partner/presentation/themes/indicators/text_field_theme.dart'; import '../../../../core/config/env.dart'; @@ -159,6 +160,16 @@ class StaffState extends ConsumerState { if (response.statusCode == 200) { print("Staff submitted successfully!"); print("Response: ${response.body}"); + + isUpdating + ? ToastHelper.showSuccessToast( + context, + 'Staff Updated Successfully', + ) + : ToastHelper.showSuccessToast( + context, + 'Staff Created Successfully', + ); context.go(AppRoutes.staffLst); } else { final responseBody = jsonDecode(response.body); diff --git a/lib/presentation/screens/UserManagement/Staff/staffList.dart b/lib/presentation/screens/UserManagement/Staff/staffList.dart index ebe2689..3f8794b 100644 --- a/lib/presentation/screens/UserManagement/Staff/staffList.dart +++ b/lib/presentation/screens/UserManagement/Staff/staffList.dart @@ -434,37 +434,68 @@ class StaffListState extends ConsumerState { Expanded( flex: 1, - child: Row( - children: [ - PopupMenuButton( - color: Colors.white, - padding: EdgeInsets.zero, - offset: Offset(0, 30), - icon: Icon( - Icons.more_vert, - color: Color(0xFF475569), - size: 14, - ), - itemBuilder: (context) => [ - if (item['is_active'] == "1") - CustomPopupMenuEntry( - child: Container( - padding: EdgeInsets.symmetric( - horizontal: 8, - vertical: 8, - ), - child: Column( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.center, - children: _buildPopupMenuActions(context, item), - ), + child: item['is_active'] == "1" + ? Row( + children: [ + GestureDetector( + onTap: () { + // Navigator.pop(context); + print('EDITStaff - ${item['id']}'); + dynamic id = item['id']; + context.go('/staff/$id'); + }, + child: Image.asset( + "assets/miscellaneous/Edit.png", + height: 15, + width: 15, ), ), - ], - ), - ], - ), + ], + ) + : Row( + children: [ + Image.asset( + "assets/miscellaneous/Edit_muted.png", + height: 15, + width: 15, + ), + ], + ), ), + + // Expanded( + // flex: 1, + // child: Row( + // children: [ + // PopupMenuButton( + // color: Colors.white, + // padding: EdgeInsets.zero, + // offset: Offset(0, 30), + // icon: Icon( + // Icons.more_vert, + // color: Color(0xFF475569), + // size: 14, + // ), + // itemBuilder: (context) => [ + // if (item['is_active'] == "1") + // CustomPopupMenuEntry( + // child: Container( + // padding: EdgeInsets.symmetric( + // horizontal: 8, + // vertical: 8, + // ), + // child: Column( + // mainAxisSize: MainAxisSize.min, + // mainAxisAlignment: MainAxisAlignment.center, + // children: _buildPopupMenuActions(context, item), + // ), + // ), + // ), + // ], + // ), + // ], + // ), + // ), ], ), ); diff --git a/lib/presentation/screens/dashboard/dashboard.dart b/lib/presentation/screens/dashboard/dashboard.dart index ab295b4..af9c90d 100644 --- a/lib/presentation/screens/dashboard/dashboard.dart +++ b/lib/presentation/screens/dashboard/dashboard.dart @@ -1,10 +1,38 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:google_fonts/google_fonts.dart'; +import 'package:intl/intl.dart'; +import '../../../core/responsive/responsive_builder.dart'; +import '../../../core/services/api_service.dart'; import '../../layouts/main_layout.dart'; import '../../providers/manager_provider.dart'; import '../../providers/userRoleProvider.dart'; +final enquiriesProvider = Provider>>((ref) { + return List.generate(5, (i) { + return { + "reg": "TN64V4387", + "company": "New India", + "date": "09/09/2025", + "time": "11:30 AM", + }; + }); +}); + +String formatToCroresLakhsAndThousands(String value) { + double numValue = double.parse(value); + + if (numValue >= 10000000) { + return "₹ ${(numValue / 10000000).toStringAsFixed(2)} Crores"; + } else if (numValue >= 100000) { + return "₹ ${(numValue / 100000).toStringAsFixed(2)} Lakhs"; + } else if (numValue >= 1000) { + return "₹ ${(numValue / 1000).toStringAsFixed(2)} Thousands"; + } else { + return "₹ $numValue"; + } +} + class DashboardScreen extends ConsumerStatefulWidget { const DashboardScreen({super.key}); @@ -13,80 +41,406 @@ class DashboardScreen extends ConsumerStatefulWidget { } class _DashboardScreenState extends ConsumerState { + int selectedIndex = 1; + int switcherStatus = 1; + late ApiService apiService; + String? policiesIssuedToday; + String? policiesIssuedYear; + String? policiesIssuedMonth; + String? premiumValueToday; + String? premiumValueYear; + String? premiumValueMonth; + String? earningsToday; + String? earningsMonth; + String? earningsYear; + String? quotationsPendingCounts; // cross verify + + // List> dataVal = []; + List> getStaffData = []; + List> originalData = []; + List> filteredData = []; + + List> quotationsPendingList = []; // cross verify + List> policiesPendingList = []; // cross verify + List> agentsPerformanceList = []; // cross verify + List> nonAgentsPerformanceList = []; // cross verify + + bool isLoading = false; + + @override + void initState() { + super.initState(); + + apiService = ApiService(); + + Future.microtask(() { + // final id = ref.read(managerIdProvider); + final id = ref.read(userIdProvider); + final roleId = ref.read(userRoleProvider); + + print("ENQroleId - $roleId"); + if (id != null) { + getStaffList(id, roleId); + } + }); + } + + Future getStaffList(int managerId, role) async { + print('getClaimList called MagId - $managerId - $role'); + setState(() { + isLoading = true; + }); + + try { + final response = await apiService.fetchDashboard(managerId, role); + + if (response['status'] == 'success') { + final data = response['data']; + print('go to 81 - getStaffListData - ${response['data']}'); + setState(() { + policiesIssuedToday = data['policies_issued']['today'].toString(); + policiesIssuedYear = data['policies_issued']['year'].toString(); + policiesIssuedMonth = data['policies_issued']['month'].toString(); + premiumValueToday = data['premium_value']['today'].toString(); + premiumValueYear = data['premium_value']['year'].toString(); + premiumValueMonth = data['premium_value']['month'].toString(); + earningsToday = data['earnings']['today'].toString(); + earningsYear = data['earnings']['year'].toString(); + earningsMonth = data['earnings']['month'].toString(); + + // agents_performance + if (data['agents_performance'] != null) { + agentsPerformanceList = List>.from( + (data['agents_performance'] as List).map( + (item) => Map.from(item), + ), + ); + agentsPerformanceList.sort((a, b) { + int aValue = a['total_policis_pending'] ?? 0; + int bValue = b['total_policis_pending'] ?? 0; + return aValue.compareTo(bValue); // ascending + }); + } + + //nonAgentsPerformanceList + if (data['agents_performance'] != null) { + nonAgentsPerformanceList = List>.from( + (data['agents_performance'] as List).map( + (item) => Map.from(item), + ), + ); + + // Sort descending + nonAgentsPerformanceList.sort((a, b) { + int aValue = a['total_policy_pending'] ?? 0; + int bValue = b['total_policy_pending'] ?? 0; + return bValue.compareTo(aValue); // descending + }); + } + + if (data['policies_pending'] != null) { + policiesPendingList = List>.from( + (data['policies_pending'] as List).map( + (item) => Map.from(item), + ), + ); + } + + if (data['quotations_pending'] != null) { + quotationsPendingList = List>.from( + (data['quotations_pending'] as List).map( + (item) => Map.from(item), + ), + ); + } + }); + } else { + getStaffData = []; + originalData = []; + } + } catch (e) { + print('Exception occurred: $e'); + } finally { + setState(() { + isLoading = false; + }); + } + } + @override Widget build(BuildContext context) { + final enquiries = ref.watch(enquiriesProvider); final managerId = ref.watch(managerIdProvider); final userId = ref.watch(userIdProvider); final role = ref.watch(userRoleProvider); return MainLayout( title: "Home", - body: Row( - children: [ - // Main content - Expanded( - child: Column( - children: [ - // Stats cards - Padding( - padding: const EdgeInsets.all(16.0), - child: Row( - children: const [ - Expanded( - child: StatCard( - title: "Policies Issued", - today: "20", - month: "600", - year: "219000", - imagePath: "assets/dashboard/Policy issue icon.png", - ), + body: ResponsiveBuilder.isMobile(context) + ? Expanded( + // ✅ take available space + child: SingleChildScrollView( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // 🔹 Stats cards stacked + Padding( + padding: const EdgeInsets.all(8.0), + child: StatCard( + title: "Policies Issued", + today: policiesIssuedToday ?? '', + month: policiesIssuedMonth ?? '', + year: policiesIssuedYear ?? '', + imagePath: "assets/dashboard/Policy issue icon.png", ), - SizedBox(width: 16), - Expanded( - child: StatCard( - title: "Premium Value", - today: "₹21000", - month: "₹630000", - year: "₹239950000", - imagePath: "assets/dashboard/Premium value.png", - ), + ), + // const SizedBox(height: 12), + Padding( + padding: const EdgeInsets.all(8.0), + child: StatCard( + title: "Premium Value", + today: premiumValueToday ?? '', + month: premiumValueMonth ?? '', + year: premiumValueYear ?? '', + imagePath: "assets/dashboard/Policy issue icon.png", ), - SizedBox(width: 16), - Expanded( - child: StatCard( - title: "Earnings", - today: "₹2100", - month: "₹63000", - year: "₹23995000", - imagePath: - "assets/dashboard/Earnings.png", // uses image - ), + ), + // const SizedBox(height: 12), + Padding( + padding: const EdgeInsets.all(8.0), + child: StatCard( + title: "Earnings", + today: earningsToday ?? '', + month: earningsMonth ?? '', + year: earningsYear ?? '', + imagePath: "assets/dashboard/Policy issue icon.png", ), - ], - ), - ), + ), - // Tables - Expanded( - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 16.0), - child: Row( - children: const [ + const SizedBox(height: 20), + + Container( + padding: const EdgeInsets.all(16.0), + decoration: BoxDecoration( + color: Color(0XFFEDF6F5), + borderRadius: const BorderRadius.only( + topLeft: Radius.circular(50), // adjust radius + topRight: Radius.circular(50), + ), + ), + // 🔹 Tab Switcher + child: Column( + children: [ + Container( + decoration: BoxDecoration( + color: const Color(0xFF425B5B), + borderRadius: BorderRadius.circular(25), + ), + padding: const EdgeInsets.all(8), + child: Row( + children: [ + buildTab("Enquiries Pending", 0), + const SizedBox(width: 10), + buildTab("Quotation Pending", 1), + ], + ), + ), + + const SizedBox(height: 20), + + // 🔹 Scrollable list + ListView.builder( + itemCount: enquiries.length, + shrinkWrap: true, + physics: + const NeverScrollableScrollPhysics(), // disable inner scroll + itemBuilder: (context, index) { + final item = enquiries[index]; + return Card( + elevation: 2, + margin: const EdgeInsets.symmetric(vertical: 8), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), + ), + color: Colors.white, + child: ListTile( + title: Text( + item["reg"]!, + style: GoogleFonts.inter( + fontWeight: FontWeight.bold, + fontSize: 12, + ), + ), + subtitle: Text( + item["company"]!, + style: GoogleFonts.inter( + fontSize: 12, + fontWeight: FontWeight.bold, + ), + ), + trailing: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + item["date"]!, + style: GoogleFonts.inter(fontSize: 12), + ), + Text( + item["time"]!, + style: GoogleFonts.inter(fontSize: 12), + ), + ], + ), + ), + ); + }, + ), + ], + ), + ), + ], + ), + ), + ) + : Expanded( + child: Column( + children: [ + if (role != 'staff') + Row( + children: [ Expanded( - child: DataSection(title: "Enquiries Pending (15)"), + child: StatCard( + title: "Policies Issued", + today: policiesIssuedToday ?? '', + month: policiesIssuedMonth ?? '', + year: policiesIssuedYear ?? '', + imagePath: "assets/dashboard/Policy issue icon.png", + ), ), SizedBox(width: 16), Expanded( - child: DataSection(title: "Quotations Pending (10)"), + child: StatCard( + title: "Premium Value", + today: premiumValueToday ?? '', + month: premiumValueMonth ?? '', + year: premiumValueYear ?? '', + imagePath: "assets/dashboard/Premium value.png", + ), + ), + SizedBox(width: 16), + Expanded( + child: StatCard( + title: "Earnings", + today: earningsToday ?? '', + month: earningsMonth ?? '', + year: earningsYear ?? '', + imagePath: "assets/dashboard/Earnings.png", + ), + ), + ], + ), + if (role == 'manager') + Expanded( + child: Row( + children: [ + Expanded( + child: Performance( + title: "Top Agent Performance", + data: agentsPerformanceList, + ), + ), + SizedBox(width: 16), + Expanded( + child: Performance( + title: "Bottom Non-Performing Agents", + data: nonAgentsPerformanceList, + ), + ), + ], + ), + ), + const SizedBox(height: 10), + Expanded( + child: Row( + children: [ + Expanded( + child: (role == 'agent') + ? agentPendings( + title: + "Enquiries Pending (${policiesPendingList.length})", + data: policiesPendingList, + ) + : othersPendings( + title: + "Quotations Pending (${quotationsPendingList.length})", + data: quotationsPendingList, + stringFlag: "Quotation", + ), + ), + const SizedBox(width: 16), + Expanded( + child: (role == 'agent') + ? agentPendings( + title: + "Quotations Pending (${quotationsPendingList.length})", + data: quotationsPendingList, + ) + : othersPendings( + title: + "Policies Pending (${policiesPendingList.length})", + data: policiesPendingList, + stringFlag: "Policies", + ), + // child: + // quotationsPendingList.isEmpty ? Center(child: Text("Loading quotations...")) : + // agentPendings( + // title: "Quotations Pending (${quotationsPendingList.length})", + // data: quotationsPendingList, + // ), ), ], ), ), - ), - ], + ], + ), + ), + ); + } + + Widget buildTab(String title, int index) { + final isSelected = selectedIndex == index + 1; + return GestureDetector( + onTap: () { + setState(() { + selectedIndex = index + 1; + if (selectedIndex == 2) { + switcherStatus = 0; + } else { + switcherStatus = 1; + } + }); + }, + child: SizedBox( + child: Container( + padding: EdgeInsets.only( + top: 9.0, + bottom: 9.0, + left: 20.0, + right: 20.0, + ), + decoration: BoxDecoration( + color: isSelected ? const Color(0xFFFFFFFF) : Colors.transparent, + borderRadius: BorderRadius.circular(16), + ), + child: Text( + title, + style: GoogleFonts.poppins( + fontSize: 12, + color: isSelected ? Colors.black : Colors.white, + fontWeight: FontWeight.w500, ), ), - ], + ), ), ); } @@ -117,6 +471,7 @@ class StatCard extends StatelessWidget { shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), child: Padding( padding: const EdgeInsets.all(16.0), + child: Column( crossAxisAlignment: CrossAxisAlignment.center, children: [ @@ -204,24 +559,170 @@ class StatCard extends StatelessWidget { } } -// Table section widget -class DataSection extends StatelessWidget { +class agentPendings extends StatelessWidget { final String title; - const DataSection({super.key, required this.title}); + final List> data; + const agentPendings({Key? key, required this.title, required this.data}) + : super(key: key); @override Widget build(BuildContext context) { - final data = List.generate( - 10, - (i) => { - "date": "12/09/2025 11:00 AM", - "reg": "TN64V4387", - "company": "New India", - }, - ); + print('-Data: $data'); + // final data = List.generate( + // 10, + // (i) => { + // "date": "12/09/2025", + // "time": "11.30", + // "reg": "TN64V438777", + // "company": "New India", + // }, + // ); + // return Card( + // color: const Color(0xFFEAF6F4), // light mint background + // elevation: 0, + // shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), + // child: Padding( + // padding: const EdgeInsets.all(12.0), + // child: Column( + // crossAxisAlignment: CrossAxisAlignment.start, + // children: [ + // Row( + // mainAxisAlignment: MainAxisAlignment.spaceBetween, + // children: [ + // Text( + // title, + // style: GoogleFonts.inter( + // fontWeight: FontWeight.bold, + // fontSize: 20, + // color: Color(0xff425B5B), + // ), + // ), + // ], + // ), + // const SizedBox(height: 20), + // Container( + // padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 12), + // decoration: BoxDecoration( + // color: const Color(0xFF3E5B56), + // borderRadius: BorderRadius.circular(10), + // ), + // child: Row( + // mainAxisAlignment: MainAxisAlignment.spaceBetween, + // children: [ + // Expanded( + // child: Text( + // "Date & Time", + // textAlign: TextAlign.center, + // style: GoogleFonts.inter( + // fontWeight: FontWeight.w600, + // fontSize: 18, + // color: Colors.white, + // ), + // ), + // ), + // Expanded( + // child: Text( + // "Registration number", + // textAlign: TextAlign.center, + // style: GoogleFonts.inter( + // fontWeight: FontWeight.w600, + // fontSize: 18, + // color: Colors.white, + // ), + // ), + // ), + // Expanded( + // child: Text( + // "Insurer Company", + // textAlign: TextAlign.center, + // style: GoogleFonts.inter( + // fontWeight: FontWeight.w600, + // fontSize: 18, + // color: Colors.white, + // ), + // ), + // ), + // ], + // ), + // ), + // Expanded( + // child: ListView( + // children: [ + // const SizedBox(height: 4), + // + // ...data.map( + // (row) => Container( + // margin: const EdgeInsets.symmetric(vertical: 5), + // padding: const EdgeInsets.symmetric( + // vertical: 5, + // horizontal: 12, + // ), + // decoration: BoxDecoration( + // color: Colors.white, + // borderRadius: BorderRadius.circular(10), + // ), + // child: Row( + // mainAxisAlignment: MainAxisAlignment.spaceBetween, + // children: [ + // Expanded( + // child: Center( + // child: Column( + // mainAxisAlignment: MainAxisAlignment.start, + // crossAxisAlignment: CrossAxisAlignment.start, + // children: [ + // Text( + // row["date"]!, + // style: GoogleFonts.inter( + // fontSize: 16, + // fontWeight: FontWeight.w500, + // ), + // ), + // Text( + // row["time"]!, + // style: GoogleFonts.inter( + // fontSize: 10, + // fontWeight: FontWeight.w400, + // ), + // ), + // ], + // ), + // ), + // ), + // + // Expanded( + // child: Text( + // row["reg"]!, + // textAlign: TextAlign.center, + // style: GoogleFonts.inter( + // fontSize: 16, + // fontWeight: FontWeight.w500, + // ), + // ), + // ), + // Expanded( + // child: Text( + // row["company"]!, + // textAlign: TextAlign.center, + // style: GoogleFonts.inter( + // fontSize: 16, + // fontWeight: FontWeight.w500, + // ), + // ), + // ), + // ], + // ), + // ), + // ), + // ], + // ), + // ), + // ], + // ), + // ), + // ); return Card( - color: const Color(0xFFEAF6F4), // light mint background + color: const Color(0xFFEAF6F4), elevation: 0, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), child: Padding( @@ -229,15 +730,18 @@ class DataSection extends StatelessWidget { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ + // Title Text( title, style: GoogleFonts.inter( fontWeight: FontWeight.bold, fontSize: 20, - color: Color(0xff425B5B), + color: const Color(0xff425B5B), ), ), const SizedBox(height: 20), + + // Header row Container( padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 12), decoration: BoxDecoration( @@ -245,7 +749,6 @@ class DataSection extends StatelessWidget { borderRadius: BorderRadius.circular(10), ), child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Expanded( child: Text( @@ -283,75 +786,445 @@ class DataSection extends StatelessWidget { ], ), ), - Expanded( - child: ListView( - children: [ - const SizedBox(height: 4), + const SizedBox(height: 8), - ...data.map( - (row) => Container( - margin: const EdgeInsets.symmetric(vertical: 5), - padding: const EdgeInsets.symmetric( - vertical: 5, - horizontal: 12, - ), - decoration: BoxDecoration( - color: Colors.white, - borderRadius: BorderRadius.circular(10), - ), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Expanded( - child: Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - '2025-05-31', - style: GoogleFonts.inter( - fontSize: 16, - fontWeight: FontWeight.w500, - ), - ), - Text( - '11:11 pm', - style: GoogleFonts.inter( - fontSize: 10, - fontWeight: FontWeight.w400, - ), - ), - ], - ), - ), - ), + // List of rows + Flexible( + child: ListView.builder( + itemCount: data.length, + itemBuilder: (context, index) { + final row = data[index]; - Expanded( - child: Text( - row["reg"]!, - textAlign: TextAlign.center, - style: GoogleFonts.inter( - fontSize: 16, - fontWeight: FontWeight.w500, - ), - ), - ), - Expanded( - child: Text( - row["company"]!, - textAlign: TextAlign.center, - style: GoogleFonts.inter( - fontSize: 16, - fontWeight: FontWeight.w500, - ), - ), - ), - ], - ), + // Split date & time from created_on + String date = ""; + String time = ""; + if ((row['created_on'] ?? "").isNotEmpty) { + final parts = row['created_on'].split(' '); + date = parts[0]; + if (parts.isNotEmpty) { + DateTime parsedDate = DateTime.parse(parts[0]); + date = DateFormat( + 'dd/MM/yyyy', + ).format(parsedDate); // 19/09/2025 + } + time = parts.length > 1 ? parts[1] : ""; + } + + return Container( + margin: const EdgeInsets.symmetric(vertical: 5), + padding: const EdgeInsets.symmetric( + vertical: 5, + horizontal: 12, ), - ), - ], + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(10), + ), + child: Row( + children: [ + // Date & Time + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + date, + style: GoogleFonts.inter( + fontSize: 16, + fontWeight: FontWeight.w500, + ), + ), + Text( + time, + style: GoogleFonts.inter( + fontSize: 10, + fontWeight: FontWeight.w400, + ), + ), + ], + ), + ), + + // Registration number + Expanded( + child: Text( + row['reg_no'] ?? "", + textAlign: TextAlign.center, + style: GoogleFonts.inter( + fontSize: 16, + fontWeight: FontWeight.w500, + ), + ), + ), + + // Insurer company + Expanded( + child: Text( + row['insurer_name'] ?? "", + textAlign: TextAlign.center, + style: GoogleFonts.inter( + fontSize: 16, + fontWeight: FontWeight.w500, + ), + ), + ), + ], + ), + ); + }, + ), + ), + ], + ), + ), + ); + } +} + +class othersPendings extends StatelessWidget { + final String title; + final String stringFlag; + final List> data; + + othersPendings({ + Key? key, + required this.title, + required this.data, + required this.stringFlag, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + print(' Data: $data'); + + return Card( + color: const Color(0xFFEAF6F4), + elevation: 0, + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), + child: Padding( + padding: const EdgeInsets.all(12.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // Title + Text( + title, + style: GoogleFonts.inter( + fontWeight: FontWeight.bold, + fontSize: 20, + color: const Color(0xff425B5B), + ), + ), + const SizedBox(height: 20), + + // Header row + Container( + padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 12), + decoration: BoxDecoration( + color: const Color(0xFF3E5B56), + borderRadius: BorderRadius.circular(10), + ), + child: Row( + children: [ + Expanded( + child: Text( + "S.No", + textAlign: TextAlign.center, + style: GoogleFonts.inter( + fontWeight: FontWeight.w600, + fontSize: 18, + color: Colors.white, + ), + ), + ), + Expanded( + child: Text( + "Staff Name", + textAlign: TextAlign.center, + style: GoogleFonts.inter( + fontWeight: FontWeight.w600, + fontSize: 18, + color: Colors.white, + ), + ), + ), + Expanded( + child: Text( + stringFlag + " Issued", + textAlign: TextAlign.center, + style: GoogleFonts.inter( + fontWeight: FontWeight.w600, + fontSize: 18, + color: Colors.white, + ), + ), + ), + (stringFlag == "Policies") + ? Expanded( + child: Text( + "Total Premium Value", + textAlign: TextAlign.center, + style: GoogleFonts.inter( + fontWeight: FontWeight.w600, + fontSize: 18, + color: Colors.white, + ), + ), + ) + : const SizedBox.shrink(), + ], + ), + ), + const SizedBox(height: 8), + + // List of rows + Flexible( + child: ListView.builder( + itemCount: data.length, + itemBuilder: (context, index) { + final row = data[index]; + + final issuedCount = stringFlag == "Policies" + ? (row['total_policis_pending'] ?? 0).toString() + : (row['total_quotation_pending'] ?? 0).toString(); + + final permiumValue = row['total_premium_value'] == null + ? '0' + : row['total_premium_value']; + + return Container( + margin: const EdgeInsets.symmetric(vertical: 5), + padding: const EdgeInsets.symmetric( + vertical: 5, + horizontal: 12, + ), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(10), + ), + child: Row( + children: [ + Expanded( + child: Text( + (index + 1).toString(), + textAlign: TextAlign.center, + style: GoogleFonts.inter( + fontSize: 16, + fontWeight: FontWeight.w500, + ), + ), + ), + Expanded( + child: Text( + row['staff_name'] ?? "", + textAlign: TextAlign.center, + style: GoogleFonts.inter( + fontSize: 16, + fontWeight: FontWeight.w500, + ), + ), + ), + + Expanded( + child: Text( + issuedCount, + textAlign: TextAlign.center, + style: GoogleFonts.inter( + fontSize: 16, + fontWeight: FontWeight.w500, + ), + ), + ), + stringFlag == "Policies" + ? Expanded( + child: Text( + formatToCroresLakhsAndThousands(permiumValue), + textAlign: TextAlign.center, + style: GoogleFonts.inter( + fontSize: 16, + fontWeight: FontWeight.w500, + ), + ), + ) + : const SizedBox.shrink(), + ], + ), + ); + }, + ), + ), + ], + ), + ), + ); + } +} + +class Performance extends StatelessWidget { + final String title; + final List> data; + + Performance({Key? key, required this.title, required this.data}) + : super(key: key); + + @override + Widget build(BuildContext context) { + return Card( + color: const Color(0xFFEAF6F4), + elevation: 0, + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), + child: Padding( + padding: const EdgeInsets.all(12.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // Title + Text( + title, + style: GoogleFonts.inter( + fontWeight: FontWeight.bold, + fontSize: 20, + color: const Color(0xff425B5B), + ), + ), + const SizedBox(height: 20), + + // Header row + Container( + padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 12), + decoration: BoxDecoration( + color: const Color(0xFF3E5B56), + borderRadius: BorderRadius.circular(10), + ), + child: Row( + children: [ + Expanded( + child: Text( + "S.No", + textAlign: TextAlign.center, + style: GoogleFonts.inter( + fontWeight: FontWeight.w600, + fontSize: 18, + color: Colors.white, + ), + ), + ), + Expanded( + child: Text( + "Agent Name", + textAlign: TextAlign.center, + style: GoogleFonts.inter( + fontWeight: FontWeight.w600, + fontSize: 18, + color: Colors.white, + ), + ), + ), + Expanded( + child: Text( + "Policies Issued", + textAlign: TextAlign.center, + style: GoogleFonts.inter( + fontWeight: FontWeight.w600, + fontSize: 18, + color: Colors.white, + ), + ), + ), + Expanded( + child: Text( + title == "Top Agent Performance" + ? "Premium Value" + : "Status", + textAlign: TextAlign.center, + style: GoogleFonts.inter( + fontWeight: FontWeight.w600, + fontSize: 18, + color: Colors.white, + ), + ), + ), + ], + ), + ), + const SizedBox(height: 8), + + // List of rows + Flexible( + child: ListView.builder( + itemCount: data.length, + itemBuilder: (context, index) { + final row = data[index]; + + return Container( + margin: const EdgeInsets.symmetric(vertical: 5), + padding: const EdgeInsets.symmetric( + vertical: 5, + horizontal: 12, + ), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(10), + ), + child: Row( + children: [ + Expanded( + child: Text( + (index + 1).toString(), // convert int to string + textAlign: TextAlign.center, + style: GoogleFonts.inter( + fontSize: 16, + fontWeight: FontWeight.w500, + ), + ), + ), + + Expanded( + child: Text( + row['agent_name'] ?? "", + textAlign: TextAlign.center, + style: GoogleFonts.inter( + fontSize: 16, + fontWeight: FontWeight.w500, + ), + ), + ), + + Expanded( + child: Text( + row['total_policy_issued'] ?? "", + textAlign: TextAlign.center, + style: GoogleFonts.inter( + fontSize: 16, + fontWeight: FontWeight.w500, + ), + ), + ), + + Expanded( + child: Text( + title == "Top Agent Performance" + ? formatToCroresLakhsAndThousands( + row['total_premium_value'] ?? 0, + ) + : (row['status']?.toString() ?? ""), + textAlign: TextAlign.center, + style: GoogleFonts.inter( + fontSize: 16, + fontWeight: FontWeight.w500, + color: title == "Top Agent Performance" + ? Colors.black + : (row['status']?.toString().toLowerCase() == + "active" + ? Colors.green + : Colors.red), + ), + ), + ), + ], + ), + ); + }, ), ), ], diff --git a/lib/presentation/screens/staff/Enquiry/enquiry_list.dart b/lib/presentation/screens/staff/Enquiry/enquiry_list.dart new file mode 100644 index 0000000..973f379 --- /dev/null +++ b/lib/presentation/screens/staff/Enquiry/enquiry_list.dart @@ -0,0 +1,914 @@ +// import 'dart:io' as html; +import 'package:http/http.dart' as http; +import 'package:dropdown_search/dropdown_search.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:go_router/go_router.dart'; +import 'package:google_fonts/google_fonts.dart'; +import 'package:intl/intl.dart'; + +import '../../../../core/config/env.dart'; +import '../../../../core/routing/routes.dart'; +import '../../../../core/services/api_service.dart'; +import '../../../../data/services/auth_service.dart'; + +import 'package:toastification/toastification.dart'; +import 'package:universal_html/html.dart' as html; + +import '../../../../core/routing/routes.dart'; +import '../../../../core/services/api_service.dart'; +import '../../../../data/services/auth_service.dart'; +import '../../../../data/utils/Pagination.dart'; +import '../../../../data/utils/toastNotification.dart'; +import '../../../../data/utils/validators.dart'; + +import '../../../layouts/main_layout.dart'; +import '../../../layouts/responsive_layout.dart'; +import '../../../providers/manager_provider.dart'; +import '../../../providers/quotation_staff_proivder.dart'; +import '../../../providers/userRoleProvider.dart'; +import '../../../themes/indicators/customizd_file_upload.dart'; +import '../../../themes/indicators/date_field_theme.dart'; +import '../../../themes/indicators/export_btn.dart'; +import '../../../themes/indicators/search_field_theme.dart'; +import '../../../themes/indicators/text_field_theme.dart'; + +import 'package:file_picker/file_picker.dart'; + +import '../../../widgets/custom_action_popup.dart'; +import '../assignStaff.dart'; + +class EnquiryStaff extends ConsumerStatefulWidget { + const EnquiryStaff({super.key}); + @override + ConsumerState createState() => EnquiryStaffState(); +} + +class EnquiryStaffState extends ConsumerState { + int currentPage = 1; + int itemsPerPage = 10; + late ApiService apiService; + + // List> dataVal = []; + List> getStaffData = []; + List> originalData = []; + List> filteredData = []; + bool isLoading = false; + dynamic roleId; + + @override + void initState() { + super.initState(); + + apiService = ApiService(); + + Future.microtask(() { + final id = ref.read(managerIdProvider); + roleId = ref.read(userRoleProvider); + + print("ENQroleId - $roleId"); + if (id != null) { + getStaffList(id, roleId); + } + }); + } + + void refresh() { + final id = ref.read(managerIdProvider); + final roleId = ref.read(userRoleProvider); + + if (id != null) { + getStaffList(id, roleId); + } + } + + Future getStaffList(int managerId, role) async { + print('getClaimList called MagId - $managerId - $role'); + setState(() { + isLoading = true; + }); + + try { + final response = await apiService.fetchPolicyList(managerId, role); + + if (response['status'] == 'success') { + final data = response['data']; + print('getStaffListData - ${response['data']}'); + setState(() { + if (data is List) { + // Already a list of maps + getStaffData = List>.from(data); + } else if (data is Map) { + // Single object, wrap in a list + getStaffData = [Map.from(data)]; + } else { + getStaffData = []; + } + // getStaffData = List>.from(response['data']); + originalData = getStaffData; + filteredData = List.from(originalData); + // print('originalData - $getClaimPolicies'); + }); + } else { + getStaffData = []; + originalData = []; + } + } catch (e) { + print('Exception occurred: $e'); + } finally { + setState(() { + isLoading = false; + }); + } + } + + List get _paginatedData { + // Sort descending by id first + final sortedData = [...filteredData] + ..sort((a, b) => int.parse(b['id']) - int.parse(a['id'])); + + // final sortedData = [...filteredData]; + final startIndex = (currentPage - 1) * itemsPerPage; + final endIndex = (currentPage * itemsPerPage).clamp(0, sortedData.length); + + return sortedData.sublist(startIndex, endIndex); + } + + void filterData(String query) { + print("FilterDAta - $query"); + setState(() { + filteredData = getStaffData.where((item) { + // final isActiveStatus = item['is_active'] == "1" ? "active" : "inactive"; + + return (item['agent_name'] ?? '').toLowerCase().contains( + query.toLowerCase(), + ) || + (item['reg_no'] ?? '').toLowerCase().contains( + query.toLowerCase(), + ) || + (item['insurer_name'] ?? '').toLowerCase().contains( + query.toLowerCase(), + ) || + (item['assigned_to_name'] ?? '').toLowerCase().contains( + query.toLowerCase(), + ) || + (item['insured_name'] ?? '').toLowerCase().contains( + query.toLowerCase(), + ) || + (item['premium_amount'] ?? '').toLowerCase().contains( + query.toLowerCase(), + ) || + (item['payment_mode'] ?? '').toLowerCase().contains( + query.toLowerCase(), + ) || + (_formatDate(item['updated_on']) ?? '').toLowerCase().contains( + query.toLowerCase(), + ) || + (item['policy_number'] ?? '').toLowerCase().contains( + query.toLowerCase(), + ) || + (item['status'] ?? '').toLowerCase().contains(query.toLowerCase()); + }).toList(); + }); + } + + final TextEditingController _searchStaffController = TextEditingController(); + + String _formatDate(String rawDate) { + try { + final dateTime = DateTime.parse(rawDate); + return DateFormat('dd/MM/yyyy HH:mm').format(dateTime); // 24-hour format + } catch (e) { + return rawDate; // fallback if parsing fails + } + } + + List _buildPopupMenuActions( + BuildContext context, + dynamic data, + id, + regNum, + ) { + return [ + if (roleId == 'manager') ...[ + Material( + color: Colors.transparent, + child: InkWell( + onTap: () { + Navigator.pop(context); + showDialog( + context: context, + builder: (ctx) => AssignStaffDialog( + enquiryPrimaryId: id, + regNum: regNum, + userId: 1, + onSubmit: (value) { + debugPrint("New assignY: $value"); + refresh(); + }, + ), + ); + }, + hoverColor: Color(0xFFE3F1F0), + splashColor: Color(0xFFE3F1F0), + borderRadius: BorderRadius.circular(6), + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4), + child: Row( + // mainAxisSize: MainAxisSize.min, + children: [ + Image.asset( + "assets/miscellaneous/image_3.png", + // height: 45, + // width: 15, + ), + + SizedBox(width: 10), + Text('Assign Staff'), + ], + ), + ), + ), + ), + ], + + Material( + color: Colors.transparent, + child: InkWell( + onTap: () { + Navigator.pop(context); + ref.read(quotationStaffIdProvider.notifier).state = id; + context.go(AppRoutes.quotation); + }, + hoverColor: Color(0xFFE3F1F0), + splashColor: Color(0xFFE3F1F0), + borderRadius: BorderRadius.circular(6), + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4), + child: Row( + // mainAxisSize: MainAxisSize.min, + children: [ + Image.asset( + "assets/miscellaneous/image_1.png", + height: 15, + width: 15, + ), + + const SizedBox(width: 10), + ((data['status'] == 'Policy Created') || + (data['status'] == 'Quotation Accepted')) + ? const Text('View Quotation') + : const Text('Create Quotation'), + ], + ), + ), + ), + ), + + if (data['status'] == 'Quotation Accepted' || + data['status'] == 'Policy Created') + Material( + color: Colors.transparent, + child: InkWell( + onTap: () { + Navigator.pop(context); + ref.read(quotationStaffIdProvider.notifier).state = id; + context.go(AppRoutes.policy); + }, + hoverColor: Color(0xFFE3F1F0), + splashColor: Color(0xFFE3F1F0), + borderRadius: BorderRadius.circular(6), + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4), + child: Row( + // mainAxisSize: MainAxisSize.min, + children: [ + Image.asset( + "assets/miscellaneous/image_2.png", + height: 15, + width: 15, + ), + + const SizedBox(width: 10), + data['status'] == 'Policy Created' + ? Text('View Policy') + : Text('Create Policy'), + ], + ), + ), + ), + ), + ]; + } + + @override + Widget build(BuildContext context) { + return MainLayout( + title: "Enquiries", + body: Container( + // color: Colors.yellow.shade50, + width: MediaQuery.of(context).size.width, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Container( + height: 30, + width: MediaQuery.of(context).size.width, + child: GestureDetector( + onTap: () { + context.go(AppRoutes.dashboard); + }, + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Icon( + Icons.arrow_left_sharp, + size: 35, + color: Color(0xFF425B5B), + ), + Text( + "Enquiries", + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.w600, + ), + ), + ], + ), + ), + ), + + SizedBox(height: 10), + + Expanded( + child: Container( + // color: Colors.green, + // color: Colors.green.shade50, + width: MediaQuery.of(context).size.width, + + padding: EdgeInsets.all(8.0), + child: Column( + children: [ + Container( + // height: 40, + // color: Colors.pink, + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + ThemedSearchField( + hintText: 'Search', + backgroundColor: Color(0xFFF6F8F8), + onChanged: filterData, + controller: _searchStaffController, + txtwidth: ResponsiveLayout.isMobile(context) + ? MediaQuery.of(context).size.width * 0.6 + : MediaQuery.of(context).size.width * 0.2, + ), + + Spacer(), + + ExportBtn( + sheetName: "Policy", + fileName: "Policy_list", + data: filteredData, + txt: !ResponsiveLayout.isMobile(context) + ? true + : false, + headers: [ + "reg_no", + "agent_name", + "assigned_to_name", + "insurer_name", + "insured_name", + "premium_amount", + "payment_mode", + "updated_on", + "status", + ], + ), + + // SizedBox(width: 10), + // GestureDetector( + // onTap: () { + // // print('Export'); + // }, + // child: Container( + // padding: EdgeInsets.all(8.0), + // decoration: BoxDecoration( + // color: Color(0xFF425B5B), + // borderRadius: BorderRadius.circular(8.0), + // ), + // child: Row( + // mainAxisSize: MainAxisSize.min, + // children: [ + // Icon(Icons.add, color: Colors.white), + // + // if (!ResponsiveLayout.isMobile(context)) ...[ + // SizedBox(width: 10), + // + // GestureDetector( + // onTap: () { + // // context.go(AppRoutes.agent / create); + // context.go('/staff/create'); + // }, + // child: Text( + // 'Create New Staff', + // style: TextStyle( + // color: Colors.white, + // fontWeight: FontWeight.w600, + // fontSize: 14, + // ), + // ), + // ), + // ], + // ], + // ), + // ), + // ), + ], + ), + ), + SizedBox(height: 10), + if (!ResponsiveLayout.isMobile(context)) + Container( + decoration: BoxDecoration( + color: Color(0xFFEDF6F5), + borderRadius: BorderRadius.circular(6), + ), + padding: const EdgeInsets.symmetric( + vertical: 12, + horizontal: 16, + ), + child: const Row( + children: [ + Expanded( + flex: 2, + child: Text('Date', style: _headerStyle), + ), + + Expanded( + flex: 2, + child: Text('Reg.No', style: _headerStyle), + ), + Expanded( + flex: 2, + child: Text('Agents', style: _headerStyle), + ), + Expanded( + flex: 2, + child: Text('Assigned To', style: _headerStyle), + ), + Expanded( + flex: 2, + child: Text('Insurer', style: _headerStyle), + ), + Expanded( + flex: 2, + child: Text('Insured Name', style: _headerStyle), + ), + Expanded( + flex: 2, + child: Text('Premium', style: _headerStyle), + ), + Expanded( + flex: 2, + child: Text('Payment Mode', style: _headerStyle), + ), + Expanded( + flex: 2, + child: Text('Policy Number', style: _headerStyle), + ), + + Expanded( + flex: 3, + child: Text('Status', style: _headerStyle), + ), + Expanded( + flex: 1, + child: Text('Action', style: _headerStyle), + ), + ], + ), + ), + Expanded(child: _buildDataTable(context)), + ], + ), + ), + ), + + Container( + // height: 20, + width: MediaQuery.of(context).size.width, + // color: Colors.green.shade50, + child: PaginationControls( + currentPage: currentPage, + itemsPerPage: itemsPerPage, + // totalItems: dataVal.length, + totalItems: filteredData.length, + // activeColor: layoutColor, // your theme color + onPageChanged: (page) { + setState(() { + currentPage = page; + }); + }, + onItemsPerPageChanged: (items) { + setState(() { + itemsPerPage = items; + currentPage = 1; + }); + }, + ), + ), + ], + ), + ), + ); + } + + Widget _buildDataTable(BuildContext context) { + if (filteredData.isEmpty) { + return const SizedBox( + height: 50, + child: Center(child: Text('No available data')), + ); + } + + final sortedData = [..._paginatedData]; + return ListView.builder( + itemCount: ResponsiveLayout.isMobile(context) + ? sortedData + .length // only cards for mobile + : sortedData.length + 1, // +1 for header in desktop + itemBuilder: (context, index) { + if (!ResponsiveLayout.isMobile(context) && index == 0) { + return _buildHeader(); + } + + final startIndex = (currentPage - 1) * itemsPerPage; + final item = + sortedData[index - (ResponsiveLayout.isMobile(context) ? 0 : 1)]; + final sno = startIndex + index; + + return !ResponsiveLayout.isMobile(context) + ? _buildDataRow(item, sno) + : _buildDataCard(item, sno); + }, + ); + } + + Widget _buildHeader() { + return SizedBox.shrink(); + } + + Widget _buildDataRow(Map item, sno) { + return Container( + padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 16), + // margin: const EdgeInsets.only(top: 10), + decoration: BoxDecoration( + color: Colors.white, + // color: Color(0xFFE0F7F9), + border: const Border( + bottom: BorderSide(color: Color(0xFFEAEAEA), width: 1), + ), + borderRadius: BorderRadius.circular(8), + ), + child: Row( + children: [ + Expanded( + flex: 2, + child: Text( + _formatDate(item['updated_on']) ?? '-', + style: _dataBold, + softWrap: true, + maxLines: 3, + ), + ), + + Expanded( + flex: 2, + child: Text(item['reg_no'] ?? '-', style: _dataBold), + ), + Expanded( + flex: 2, + child: Text( + _formatDate(item['agent_name']) ?? '-', + style: _dataBold, + ), + ), + Expanded( + flex: 2, + child: Text(item['assigned_to_name'] ?? '-', style: _dataBold), + ), + Expanded( + flex: 2, + child: Text( + item['insurer_name'] ?? '-', + style: _dataBold, + softWrap: true, + maxLines: 3, + ), + ), + Expanded( + flex: 2, + child: Text( + item['insured_name'] ?? '-', + style: _dataBold, + softWrap: true, + maxLines: 3, + ), + ), + Expanded( + flex: 2, + child: Text( + item['premium_amount'] ?? '-', + style: _dataBold, + softWrap: true, + maxLines: 3, + ), + ), + Expanded( + flex: 2, + child: Text( + item['payment_mode'] ?? '-', + style: _dataBold, + softWrap: true, + maxLines: 3, + ), + ), + Expanded( + flex: 2, + child: Text( + item['policy_number'] ?? '-', + style: _dataBold, + softWrap: true, + maxLines: 3, + ), + ), + + Expanded( + flex: 3, + child: Text(item['status'] ?? '-', style: _dataBold), + ), + + Expanded( + flex: 1, + child: Row( + children: [ + PopupMenuButton( + color: Colors.white, + padding: EdgeInsets.zero, + offset: Offset(0, 30), + icon: Icon( + Icons.more_vert, + color: Color(0xFF475569), + size: 14, + ), + itemBuilder: (context) => [ + CustomPopupMenuEntry( + child: Container( + padding: EdgeInsets.symmetric( + horizontal: 8, + vertical: 8, + ), + child: Column( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: _buildPopupMenuActions( + context, + item, + item['id'], + item['reg_no'], + ), + ), + ), + ), + ], + ), + ], + ), + ), + ], + ), + ); + } + + Widget _buildDataCard(Map item, int sno) { + return Container( + margin: const EdgeInsets.symmetric(vertical: 6, horizontal: 8), + padding: const EdgeInsets.all(12), + decoration: BoxDecoration( + color: const Color(0xFFF6FEFD), + borderRadius: BorderRadius.circular(8.0), + border: Border.all(color: const Color(0xffD9EBE8)), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + /// Top row: Reg.No + Action menu + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text(item['reg_no'] ?? '-', style: _headerStyle), + PopupMenuButton( + color: Colors.white, + padding: EdgeInsets.zero, + offset: Offset(0, 30), + icon: Icon(Icons.more_vert, color: Color(0xFF475569), size: 14), + itemBuilder: (context) => [ + CustomPopupMenuEntry( + child: Container( + padding: EdgeInsets.symmetric(horizontal: 8, vertical: 8), + child: Column( + mainAxisSize: MainAxisSize.min, + children: _buildPopupMenuActions( + context, + item, + item['id'], + item['reg_no'], + ), + ), + ), + ), + ], + ), + ], + ), + const Divider(color: Color(0xffD9EBE8), thickness: 0.8), + + /// Company + Status + Row( + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text("Company", style: _cardheaderStyle), + Text(item['insurer_name'] ?? '-', style: _cardBodyStyle), + ], + ), + ), + const SizedBox(width: 5), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text("Status", style: _cardheaderStyle), + Text(item['status'] ?? '-', style: _cardBodyStyle), + ], + ), + ), + ], + ), + const SizedBox(height: 15), + + /// Agents + Assigned To + Row( + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text("Agent", style: _cardheaderStyle), + Text(item['agent_name'] ?? '-', style: _cardBodyStyle), + ], + ), + ), + const SizedBox(width: 5), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text("Assigned To", style: _cardheaderStyle), + Text( + item['assigned_to_name'] ?? '-', + style: _cardBodyStyle, + ), + ], + ), + ), + ], + ), + const SizedBox(height: 15), + + /// Insured Name + Premium + Row( + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text("Insured Name", style: _cardheaderStyle), + Text(item['insured_name'] ?? '-', style: _cardBodyStyle), + ], + ), + ), + const SizedBox(width: 5), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text("Premium", style: _cardheaderStyle), + Text(item['premium_amount'] ?? '-', style: _cardBodyStyle), + ], + ), + ), + ], + ), + const SizedBox(height: 15), + + /// Payment Mode + Policy Number + Row( + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text("Payment Mode", style: _cardheaderStyle), + Text(item['payment_mode'] ?? '-', style: _cardBodyStyle), + ], + ), + ), + const SizedBox(width: 5), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text("Policy Number", style: _cardheaderStyle), + Text(item['policy_number'] ?? '-', style: _cardBodyStyle), + ], + ), + ), + ], + ), + const SizedBox(height: 15), + + /// Date + Remarks + Row( + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text("Date", style: _cardheaderStyle), + Text( + _formatDate(item['updated_on']) ?? '-', + style: _cardBodyStyle, + ), + ], + ), + ), + const SizedBox(width: 5), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text("Remarks", style: _cardheaderStyle), + Text( + item['remarks'] ?? '-', + style: _cardBodyStyle, + maxLines: 3, + ), + ], + ), + ), + ], + ), + ], + ), + ); + } + + static final _dataBold = TextStyle( + fontSize: 14, + + fontWeight: FontWeight.w400, + color: Color(0xFF000000), + ); + + static final _dataSub = TextStyle( + fontSize: 10, + fontWeight: FontWeight.w300, + color: Color(0xFF585757), + ); + + static const _headerStyle = TextStyle( + color: Colors.black, + fontWeight: FontWeight.bold, + ); + static const _cardheaderStyle = TextStyle( + color: Colors.black, + fontWeight: FontWeight.w600, + fontSize: 12, + ); + + static const _cardBodyStyle = TextStyle( + color: Color(0xFF545454), + fontWeight: FontWeight.w400, + fontSize: 12, + ); +} diff --git a/lib/presentation/screens/staff/assignStaff.dart b/lib/presentation/screens/staff/assignStaff.dart new file mode 100644 index 0000000..9f7f66a --- /dev/null +++ b/lib/presentation/screens/staff/assignStaff.dart @@ -0,0 +1,455 @@ +import 'dart:convert'; + +import 'package:dropdown_search/dropdown_search.dart'; +import 'package:flutter/material.dart'; +import 'package:google_fonts/google_fonts.dart'; +import 'package:http/http.dart' as http; +import 'package:toastification/toastification.dart'; + +import '../../../../core/config/env.dart'; +import '../../../../core/services/api_service.dart'; +import '../../../../data/services/auth_service.dart'; +import '../../../../data/utils/toastNotification.dart'; +import '../../../../data/utils/validators.dart'; +import '../../layouts/responsive_layout.dart'; +import '../../themes/indicators/input_field_decoration.dart'; +import '../../themes/indicators/text_field_theme.dart'; + +// 🔹 Custom Dialog Widget +class AssignStaffDialog extends StatefulWidget { + final dynamic userId; + final dynamic regNum; + final dynamic enquiryPrimaryId; + final void Function(String value) onSubmit; + + const AssignStaffDialog({ + super.key, + + required this.onSubmit, + required this.regNum, + required this.userId, + required this.enquiryPrimaryId, + }); + + @override + State createState() => _AddDialogState(); +} + +class _AddDialogState extends State { + late ApiService apiService; + String? _token; + + bool isLoading = false; + bool showSuccess = false; + late TextEditingController controller; + Map controllers = {}; + + final _formKey = GlobalKey(); + final _formKeyEndrosment = GlobalKey(); + final GlobalKey>> dropDownKey = + GlobalKey>>(); + + List tabHeader = ['regNum']; + + List> getStaffDetailsData = []; + List> filteredStaffData = []; + + String? selectedStaff; + String? selectedRegNum; + String? selectedStaffName; + + Map dataDetails() { + final data = { + "id": widget.enquiryPrimaryId, + "assigned_to": selectedStaff, + "updated_by": widget.userId, + }; + return data; + } + + @override + void initState() { + super.initState(); + apiService = ApiService(); + for (String field in tabHeader) { + controllers[field] = TextEditingController(); + } + + controllers["regNum"]?.text = widget.regNum; + selectedRegNum = widget.regNum; + + // 🔹 Init logic here (API calls, token fetch, etc.) + _initializeToken(); + getStaffDetails(); + } + + Future _initializeToken() async { + _token = await AuthService.getToken(); + print("APISERTOKEN - $_token"); + } + + Future getStaffDetails() async { + print('getStaffDetails called'); + setState(() { + isLoading = true; + }); + + try { + final response = await apiService.fetchMasterDropDown('Staffs'); + + if (response['status'] == 200) { + print('getStaffDetails - ${response['data']}'); + setState(() { + getStaffDetailsData = List>.from( + response['data'], + ); + print('API Data - $getStaffDetailsData'); + + filteredStaffData = List.from(getStaffDetailsData); + print('originalData - $filteredStaffData'); + }); + } else { + getStaffDetailsData = []; + filteredStaffData = []; + } + } catch (e) { + print('Exception occurred: $e'); + } finally { + setState(() { + isLoading = false; + }); + } + } + + void handleDone() { + if (!_formKey.currentState!.validate()) return; + 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(data) async { + // final bool isUpdating = widget.id != null && widget.id != 'create'; + final String apiUrldata; + + apiUrldata = '${Env.apiUrl}enquiry/enquiryAssignUpdate'; + + // final token = await getToken(); // Fetch token + + if (_token == null) { + throw Exception('Token not found. Please log in.'); + } + + print("data------- $data}"); + + try { + final response = await http.post( + Uri.parse(apiUrldata), + headers: { + 'Authorization': 'Bearer $_token', + 'Content-Type': 'application/json', + 'app-signature': 'nhance-partner-2025-signature-35468846JRhH551HK', + }, + body: jsonEncode(data), // Convert map to JSON + ); + + if (response.statusCode == 200) { + print("Staff submitted successfully!"); + print("Response: ${response.body}"); + ToastHelper.showSuccessToast(context, 'Saved Successfully'); + + setState(() { + showSuccess = true; + }); + + // Navigator.of(context).pop(); + // + widget.onSubmit("success"); + // context.go(AppRoutes.staffLst); + } else { + final responseBody = jsonDecode(response.body); + dynamic msg = responseBody['data']; + + print("Failed to submit plan. Status: ${response.statusCode}"); + print("Error: ${response.body}"); + + ToastHelper.showErrorToast(context, 'Failed To Save'); + } + } catch (e) { + print(" Error submitting Staff: $e"); + } + } + + @override + void dispose() { + // controllers.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return AlertDialog( + backgroundColor: Colors.white, + content: showSuccess + ? Column( + mainAxisSize: MainAxisSize.min, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + GestureDetector( + onTap: () => Navigator.pop(context), + child: Container( + padding: const EdgeInsets.all(5.0), + decoration: BoxDecoration( + color: const Color(0xFFF1F1F1), + borderRadius: BorderRadius.circular(5.0), + ), + child: const Icon(Icons.close, size: 18), + ), + ), + ], + ), + + const SizedBox(height: 16), + successContent(context), + ], + ) + : Column( + mainAxisSize: MainAxisSize.min, + children: [ + // Header row + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + 'Assign Vehicle Policy', + style: GoogleFonts.inter( + color: const Color(0xFF374141), + fontSize: 18, + fontWeight: FontWeight.bold, + ), + ), + GestureDetector( + onTap: () => Navigator.pop(context), + child: Container( + padding: const EdgeInsets.all(5.0), + decoration: BoxDecoration( + color: const Color(0xFFF1F1F1), + borderRadius: BorderRadius.circular(5.0), + ), + child: const Icon(Icons.close, size: 18), + ), + ), + ], + ), + + const SizedBox(height: 16), + + // 🔹 Switch content dynamically + claims(context), + ], + ), + actions: [ + if (!showSuccess) + Center( + child: GestureDetector( + onTap: () { + handleDone(); + // widget.onSubmit(controller.text.trim()); + // Navigator.of(context).pop(); + }, + child: Container( + padding: const EdgeInsets.symmetric( + horizontal: 25.0, + vertical: 8, + ), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(8.0), + color: const Color(0xFF425B5B), + ), + child: const Text( + 'Assign Policy', + style: TextStyle(color: Colors.white), + ), + ), + ), + ), + ], + ); + } + + // 🔹 Example: Claims widget + Widget claims(BuildContext context) { + return Form( + key: _formKey, + child: Column( + children: [ + buildRegistrationNumber(context), + buildSelectStaffMem(context), + ], + ), + ); + } + + Widget successContent(BuildContext context) { + return Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Image.asset( + "assets/miscellaneous/success_img1.png", + height: 60, + width: 60, + ), + SizedBox(height: 15), + Text('Vehicle policy successfully assigned ', style: _successText), + Text( + 'to $selectedStaffName for vehicle $selectedRegNum', + style: _successText, + ), + ], + ); + } + + // ------------------------- Widget Part ----------------------------------- + Widget buildRegistrationNumber(context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text("Registration Number", style: _textStyle), + SizedBox(height: 10), + ThemedFormField( + controller: controllers['regNum']!, + backgroundColor: Color(0xFFEDF6F5), + // validator: (value) => Validators.requiredField(value, "name"), + txtwidth: ResponsiveLayout.isMobile(context) + ? null + : MediaQuery.of(context).size.width * 0.26, + readOnly: true, + ), + ], + ); + } + + Widget buildSelectStaffMem(ctx) { + // Map? selectedVehicle = filteredStaffData.firstWhere( + // (item) => item['id'].toString() == selectedStaff, + // orElse: () => {}, + // ); + + Map? selectedVehicle; + try { + selectedVehicle = filteredStaffData.firstWhere( + (item) => item['id'].toString() == selectedStaff, + ); + } catch (e) { + selectedVehicle = null; // ✅ fallback + } + + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text("Select Staff Member", style: _textStyle), + SizedBox(height: 10), + Container( + color: Colors.white, + width: ResponsiveLayout.isMobile(context) + ? null + : MediaQuery.of(context).size.width * 0.26, + // height: 40, + child: DropdownSearch>( + key: dropDownKey, + // selectedItem: selectedVehicle.isNotEmpty ? selectedVehicle : null, + selectedItem: selectedVehicle, + items: (filter, infiniteScrollProps) { + return filteredStaffData; + }, + + itemAsString: (val) => val['name'].toString(), + compareFn: (item, selectedItem) => + item['id'] == selectedItem['id'], // ✅ compare by id + validator: (val) { + if (val == null) { + return "Required"; // ✅ error message + } + return null; + }, + decoratorProps: DropDownDecoratorProps( + decoration: + AppInputDecorations.dropdownDecoration( + label: "Select Staff Member", + ).copyWith( + filled: true, + fillColor: Color( + 0xFFEDF6F5, + ), // 👈 makes the dropdown input white + ), + ), + + popupProps: PopupProps.menu( + fit: FlexFit.loose, + constraints: BoxConstraints(maxHeight: 250), + menuProps: MenuProps( + backgroundColor: + Colors.white, // 👈 sets dropdown background to white + ), + showSearchBox: true, + searchFieldProps: TextFieldProps( + decoration: InputDecoration( + filled: true, + fillColor: Colors.white, + hintText: "Search Staff ...", + 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 Staff : ${val['name']}"); + print("Id: ${val['id']}"); + selectedStaffName = val['name']; + selectedStaff = val['id']; + // controllers['agentId']?.text = val['agent_code']; + // agentId = agent['id']; + } + }, + ), + ), + ], + ); + } + + // ------------------- STyle --------------------------------- + + static final TextStyle _textStyle = TextStyle( + fontSize: 14, + fontWeight: FontWeight.w600, + ); + + static final TextStyle _successText = TextStyle( + fontSize: 18, + fontWeight: FontWeight.w600, + color: Color(0xFF177126), + ); +} diff --git a/lib/presentation/screens/staff/policy/policy.dart b/lib/presentation/screens/staff/policy/policy.dart index 5bf92ec..ecebd36 100644 --- a/lib/presentation/screens/staff/policy/policy.dart +++ b/lib/presentation/screens/staff/policy/policy.dart @@ -1,25 +1,37 @@ -import 'dart:io' as html; - +// import 'dart:io' as html; +import 'package:http/http.dart' as http; import 'package:dropdown_search/dropdown_search.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:go_router/go_router.dart'; import 'package:google_fonts/google_fonts.dart'; +import 'package:intl/intl.dart'; + +import '../../../../core/config/env.dart'; +import '../../../../core/routing/routes.dart'; +import '../../../../core/services/api_service.dart'; +import '../../../../data/services/auth_service.dart'; + +import 'package:toastification/toastification.dart'; +import 'package:universal_html/html.dart' as html; import '../../../../core/routing/routes.dart'; import '../../../../core/services/api_service.dart'; import '../../../../data/services/auth_service.dart'; import '../../../../data/utils/Pagination.dart'; +import '../../../../data/utils/toastNotification.dart'; import '../../../../data/utils/validators.dart'; import '../../../layouts/main_layout.dart'; import '../../../layouts/responsive_layout.dart'; import '../../../providers/manager_provider.dart'; +import '../../../providers/quotation_staff_proivder.dart'; import '../../../themes/indicators/customizd_file_upload.dart'; +import '../../../themes/indicators/date_field_theme.dart'; import '../../../themes/indicators/text_field_theme.dart'; - +import 'package:file_picker/file_picker.dart'; class PolicyScreen extends ConsumerStatefulWidget { const PolicyScreen({super.key}); @@ -38,7 +50,7 @@ class PolicyScreenState extends ConsumerState { late ApiService apiService; final _formKey = GlobalKey(); final GlobalKey>> dropDownKey = - GlobalKey>>(); + GlobalKey>>(); List tabHeader = [ 'name', @@ -47,21 +59,45 @@ class PolicyScreenState extends ConsumerState { 'code', 'address', 'regNo', + + 'insurer', + 'regNum', + 'idv', + 'premium_amount', + 'planType', + 'policyInsName', + 'policyPremiumAmount', + 'policyIdv', + 'policyNumber', + 'policyPaymentMode', + 'issueDate', + 'startDate', + 'endDate', ]; late String isActive = "1"; - html.File? docUploadedFile; + PlatformFile? docUploadedFile; + PlatformFile? docPDFUploadedFile; // PlatformFile? passportFile; String? selectedFileNames; + String? selectedPDFFileNames; String? passportFileUrlFromApi; + String? docUploadedFileUrlFromApi; + String? docUploadedPDFFileUrlFromApi; + String? selectedId; bool isLoading = false; bool isquotation = false; + bool hasPolicyData = false; Map controllers = {}; String? _token; dynamic userId; dynamic managerId; + dynamic enqQuotation; + dynamic selectedEnquiryId; + dynamic acceptedQuotationId; + dynamic choosedPolcyId; List> getVehicleTypeData = []; List> filteredVechicleData = []; @@ -69,20 +105,29 @@ class PolicyScreenState extends ConsumerState { List> getInsurersData = []; List> filteredInsurersData = []; + late Map enquiryData; + late Map policyData; + Map? quotationAcceptedData; + Map dataDetails() { final data = { - "name": controllers["name"]?.text, - "email": controllers["email"]?.text, - "mobile": controllers["mobile"]?.text, - "address": controllers["address"]?.text, - "agent_code": controllers["code"]?.text, - "is_active": isActive, + "quotation_id": acceptedQuotationId, + "insured_name": controllers["policyInsName"]?.text, + "issued_date": controllers["issueDate"]?.text, + "start_date": controllers["startDate"]?.text, + "end_date": controllers["endDate"]?.text, + "premium_amount": controllers["policyPremiumAmount"]?.text, + "policy_number": controllers["policyNumber"]?.text, + "payment_mode": controllers["policyPaymentMode"]?.text, "manager_id": managerId, + // "policy_pdf_file_name": "policy_doc.pdf", + // "policy_payment_receipt_file_name": "receipt_doc.pdf", + "created_by": userId, }; return data; } - List> getQuotationData = []; + List> quotationData = []; List> originalData = []; List> filteredData = []; // bool isLoading = false; @@ -97,38 +142,78 @@ class PolicyScreenState extends ConsumerState { } // updateData(); _initializeToken(); - Future.microtask(() { - managerId = ref.watch(managerIdProvider); - userId = ref.watch(userIdProvider); + // Future.microtask(() { + // managerId = ref.watch(managerIdProvider); + // userId = ref.watch(userIdProvider); + // + // getQuotationList(managerId); + // }); - getQuotationList(managerId); + Future.microtask(() { + managerId = ref.read(managerIdProvider); // ✅ use read + userId = ref.read(userIdProvider); // ✅ use read + print('QmanagerId - $managerId'); + print('QuserId - $userId'); + enqQuotation = ref.read(quotationStaffIdProvider); // ✅ use read + print('QenqQuotation - $enqQuotation'); + if (enqQuotation != null) { + _loadData(enqQuotation); + } }); } + @override + void dispose() { + // Dispose all TextEditingControllers + for (var controller in controllers.values) { + controller.dispose(); + } + super.dispose(); + } + Future _initializeToken() async { _token = await AuthService.getToken(); print("APISERTOKEN - $_token"); } - Future getQuotationList(int managerId) async { - print('getClaimList called MagId - $managerId'); + Future _loadData(enqQuotation) async { + getQuotationList(enqQuotation); + } + + Future getQuotationList(enqQuotation) async { + print('getClaimList called enqQuotation- $enqQuotation'); setState(() { isLoading = true; }); try { - final response = await apiService.fetchQuotationList(managerId); + final response = await apiService.findEnqQuotePolicyView(enqQuotation); if (response['status'] == 'success') { print('quoationListData - ${response['data']}'); setState(() { - getQuotationData = List>.from(response['data']); - originalData = getQuotationData; + enquiryData = Map.from(response['data']['enquiry']); + print('enquiryData - ${enquiryData}'); + + updateEnquiryData(enquiryData); + // quotationData = List>.from(response['data']); + quotationData = List>.from( + response['data']['quotations'], + ); + print('quotationData - ${quotationData}'); + updateQuotationData(quotationData); + + policyData = Map.from(response['data']['policies']); + + print('policyData - ${policyData}'); + + updatePolicyData(policyData); + originalData = quotationData; filteredData = List.from(originalData); // print('originalData - $getClaimPolicies'); }); } else { - getQuotationData = []; + quotationData = []; originalData = []; } } catch (e) { @@ -140,208 +225,479 @@ class PolicyScreenState extends ConsumerState { } } + void updateEnquiryData(enquiryData) { + setState(() { + selectedEnquiryId = enquiryData['id']; + print('selectedEnquiryId- $selectedEnquiryId'); + controllers["regNum"]?.text = enquiryData['reg_no']; + controllers["insurer"]?.text = enquiryData['insurer_name']; + }); + } + + void updateQuotationData(List> quotationData) { + try { + final acceptedItem = quotationData.firstWhere( + (item) => item['status'] == 'Accepted', + ); + + quotationAcceptedData = acceptedItem; + print('AcceptedData - $quotationAcceptedData'); + + if (quotationAcceptedData != null) { + if (!mounted) return; + setState(() { + acceptedQuotationId = quotationAcceptedData?['id']?.toString() ?? ''; + controllers["idv"]?.text = + quotationAcceptedData?['insured_declared_value']?.toString() ?? + ''; + controllers["premium_amount"]?.text = + quotationAcceptedData?['premium_amount']?.toString() ?? ''; + controllers["planType"]?.text = + quotationAcceptedData?['insurance_plan_type']?.toString() ?? ''; + }); + } + } catch (e) { + quotationAcceptedData = null; + print('No Accepted quotation found'); + } + } + + void updatePolicyData(Map policyData) { + if (!mounted || policyData.isEmpty) return; + + setState(() { + hasPolicyData = true; + choosedPolcyId = policyData['id']?.toString() ?? ''; + controllers["policyInsName"]?.text = + policyData['insured_name']?.toString() ?? ''; + controllers["idv"]?.text = + policyData['insured_declared_value']?.toString() ?? ''; + controllers["policyPremiumAmount"]?.text = + policyData['premium_amount']?.toString() ?? ''; + controllers["planType"]?.text = + policyData['insurance_plan_type']?.toString() ?? ''; + controllers["policyNumber"]?.text = + policyData['policy_number']?.toString() ?? ''; + controllers["policyPaymentMode"]?.text = + policyData['payment_mode']?.toString() ?? ''; + controllers["issueDate"]?.text = + policyData['issued_date']?.toString() ?? ''; + controllers["startDate"]?.text = + policyData['start_date']?.toString() ?? ''; + controllers["endDate"]?.text = policyData['end_date']?.toString() ?? ''; + + // RC FILE + String? rcPath = policyData["policy_pdf_file_name"]; + if (rcPath != null && rcPath.isNotEmpty) { + selectedPDFFileNames = rcPath.split('/').last; // UI filename + docUploadedPDFFileUrlFromApi = rcPath; // API download URL + docPDFUploadedFile = null; + // docUploadedRCFile = + // rcFileUrlFromApi as PlatformFile?; // user has not re-uploaded yet + } else { + selectedPDFFileNames = null; // no UI file shown + docUploadedPDFFileUrlFromApi = null; + docPDFUploadedFile = null; + } + + // ID PROOF FILE + String? idProofPath = policyData["policy_payment_receipt_file_name"]; + if (idProofPath != null && idProofPath.isNotEmpty) { + selectedFileNames = idProofPath.split('/').last; + docUploadedFileUrlFromApi = idProofPath; // ✅ FIXED → correct variable + docUploadedFile = null; + // docUploadedIDProof = idProofFileUrlFromApi as PlatformFile?; + } else { + selectedFileNames = null; + docUploadedFileUrlFromApi = null; + docUploadedFile = null; + } + }); + } + + void handleSave() { + if (!_formKey.currentState!.validate()) return; + setState(() { + if (_formKey.currentState!.validate()) { + dataDetails(); + final dataSet = dataDetails(); + print("dataSetPolicy - $dataSet"); + + // File validations + if ((docPDFUploadedFile == null || docPDFUploadedFile?.bytes == null) && + (docUploadedPDFFileUrlFromApi == null || + docUploadedPDFFileUrlFromApi!.isEmpty)) { + ToastHelper.showErrorToast(context, "Policy PDF is required"); + return; + } + + if ((docUploadedFile == null || docUploadedFile?.bytes == null) && + (docUploadedFileUrlFromApi == null || + docUploadedFileUrlFromApi!.isEmpty)) { + ToastHelper.showErrorToast(context, "Payment Receipt is required"); + return; + } + + // print("managerId - $managerId ,userId - $userId "); + createUserData(dataSet); + } else { + // isDi sable = false; + } + }); + } + + Future attachFiles(http.MultipartRequest request) async { + Future addFileOrKeepName( + PlatformFile? file, + String? apiFileName, + String fieldName, + ) async { + if (file != null) { + // User uploaded a new file → send as multipart + if (file.bytes != null) { + request.files.add( + http.MultipartFile.fromBytes( + fieldName, + file.bytes!, + filename: file.name, + ), + ); + } else if (file.path != null) { + request.files.add( + await http.MultipartFile.fromPath( + fieldName, + file.path!, + filename: file.name, + ), + ); + } + print("📎 Attached new file → $fieldName"); + } else if (apiFileName != null && apiFileName.isNotEmpty) { + // No new upload → tell backend to keep old file + request.fields[fieldName] = apiFileName; + print("🔗 Kept old file → $fieldName = $apiFileName"); + } else { + // Nothing at all + request.fields[fieldName] = ""; + } + } + + await addFileOrKeepName( + docPDFUploadedFile, + docUploadedPDFFileUrlFromApi, + 'policy_pdf_file_name', + ); + await addFileOrKeepName( + docUploadedFile, + docUploadedFileUrlFromApi, + 'policy_payment_receipt_file_name', + ); + } + + Future createUserData(Map userData) async { + final bool isUpdating = choosedPolcyId != null; + final id = choosedPolcyId; + // print('UPDId- $id'); + + // final bool isUpdating = false; + final uri = Uri.parse( + isUpdating + ? '${Env.apiUrl}policy/updatePolicy' + : '${Env.apiUrl}policy/createPolicy', + ); + 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'] = + 'nhance-partner-2025-signature-35468846JRhH551HK'; + + // If updating, spoof the method Laravel-style + if (isUpdating) { + request.fields['id'] = id!; + request.fields['updated_by'] = userId!.toString(); + } else { + request.fields['created_by'] = userId!.toString(); + } + + print("USerDAta - $userData"); + + // userData.forEach((key, value) { + // request.fields[key] = value.toString(); + // print("✅ Encoded travel_details2: ${request.fields[key]}"); + // }); + await attachFiles(request); + userData.forEach((key, value) { + if (key != 'policy_pdf_file_name' && + key != 'policy_payment_receipt_file_name') { + request.fields[key] = value.toString(); + print("✅ Encoded $key: ${request.fields[key]}"); + } else { + print('Something Missing..'); + } + }); + + // attach files + + // request.fields['agent_id'] = selectedId.toString(); + + print(" Sending request with fields: ${request.fields}"); + + try { + final streamedResponse = await request.send(); + final response = await http.Response.fromStream(streamedResponse); + print("Response status: ${response.statusCode}"); + print("Response body: ${response.body}"); + + if (response.statusCode == 200 || response.statusCode == 201) { + // dispose(); + print("✅ Agent submitted successfully!"); + ToastHelper.showSuccessToast(context, 'Saved Policy'); + print("Response: ${response.body}"); + + if (isUpdating) { + context.go(AppRoutes.policylist); + } else { + ref.read(enquiryIdProvider.notifier).state = null; + context.go(AppRoutes.policylist); + } + } else { + print("❌ Submission failed. Status: ${response.statusCode}"); + print("Body: ${response.body}"); + + showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + title: Text("Policy Creation Failed"), + content: Text( + "There was a problem in creating policy. Please try again.", + ), + actions: [ + TextButton( + child: Text("OK"), + onPressed: () { + Navigator.of(context).pop(); + }, + ), + ], + ); + }, + ); + } + } catch (e) { + print("🔥 Error submitting user: $e"); + } + } + @override Widget build(BuildContext context) { - return - MainLayout( - title: 'policy', - body: Container( - height: MediaQuery.of(context).size.height, - width: MediaQuery.of(context).size.width, - child: SingleChildScrollView( - scrollDirection: Axis.vertical, - child: Column( - children: [ - - Container( - height: 30, - width: MediaQuery.of(context).size.width, - child: GestureDetector( - onTap: () { - context.go(AppRoutes.dashboard); - }, - child: Row( - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisAlignment: MainAxisAlignment.start, - children: [ - Icon( - Icons.arrow_left_sharp, - size: 35, - color: Color(0xFF425B5B), + return MainLayout( + title: 'policy', + body: Container( + // height: MediaQuery.of(context).size.height, + // width: MediaQuery.of(context).size.width, + // padding: const EdgeInsets.all(20), + child: SingleChildScrollView( + scrollDirection: Axis.vertical, + child: Column( + children: [ + Container( + height: 30, + width: MediaQuery.of(context).size.width, + child: GestureDetector( + onTap: () { + context.go(AppRoutes.policylist); + }, + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Icon( + Icons.arrow_left_sharp, + size: 35, + color: Color(0xFF425B5B), + ), + Text( + "Policy", + style: GoogleFonts.inter( + fontSize: 18, + fontWeight: FontWeight.w600, ), - Text( - "Policy", - style: TextStyle( - fontSize: 18, - fontWeight: FontWeight.w600, + ), + ], + ), + ), + ), + + SizedBox(height: 10), + + //1ST SECTION + Container( + decoration: BoxDecoration( + color: const Color(0xFFEDF6F5), + borderRadius: BorderRadius.circular(10), + ), + padding: const EdgeInsets.all(20), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + "Enquiry Details", + style: GoogleFonts.inter( + fontSize: 16, + fontWeight: FontWeight.w700, + ), + ), + SizedBox(height: 15), + LayoutBuilder( + builder: (context, constraints) { + final isMobile = + constraints.maxWidth < 600; // breakpoint + + if (isMobile) { + // 📱 MOBILE → Column layout + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + registernumber(context), + const SizedBox(height: 16), + insurer(context), + const SizedBox(height: 16), + buildIdv(context), + const SizedBox(height: 16), + buildPremiumAmnt(context), + const SizedBox(height: 16), + buildPlanType(context), + const SizedBox(height: 16), + + buildDocRC(context, isMobile), + SizedBox(height: 16), + buildDocIdProof(context, isMobile), + SizedBox(height: 16), + buildDocPrevPolicy(context, isMobile), + SizedBox(height: 16), + buildDocQuotation(context, isMobile), + ], + ); + } else { + // 💻 WEB → Row layout + return Column( + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Expanded( + flex: 2, + child: registernumber(context), + ), + const SizedBox(width: 20), + Expanded(flex: 2, child: insurer(context)), + const SizedBox(width: 20), + Expanded(flex: 2, child: buildIdv(context)), + const SizedBox(width: 20), + Expanded( + flex: 2, + child: buildPremiumAmnt(context), + ), + const SizedBox(width: 20), + Expanded( + flex: 2, + child: buildPlanType(context), + ), + const SizedBox(width: 20), + ], + ), + const SizedBox(height: 20), + Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + buildDocRC(context, isMobile), + SizedBox(width: 12), + buildDocIdProof(context, isMobile), + SizedBox(width: 12), + buildDocPrevPolicy(context, isMobile), + SizedBox(width: 12), + buildDocQuotation(context, isMobile), + ], + ), + ], + ); + } + }, + ), + ], + ), + ), + + const SizedBox(height: 20), + + //2ND SECTION + Container( + // color: Colors.green, + decoration: BoxDecoration( + color: Color(0xffEDF6F5), + borderRadius: BorderRadius.circular(10.0), + ), + width: MediaQuery.of(context).size.width, + // height: MediaQuery.of(context).size.height * 0.8, + padding: EdgeInsets.all(26.0), + child: Column( + children: [ + SingleChildScrollView(child: buildFormFields(context)), + + const SizedBox(height: 20), + + //confirm button + // if (!hasPolicyData) + Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + GestureDetector( + onTap: () { + handleSave(); + }, + child: Container( + padding: EdgeInsets.symmetric( + horizontal: 45.0, + vertical: 8, + ), + + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(8.0), + color: Color(0xFF425B5B), + ), + child: Text( + 'Submit', + style: TextStyle(color: Colors.white), + ), ), ), ], ), - ), + ], ), + ), - SizedBox(height: 10), - - - //1ST SECTION - Container( - decoration: BoxDecoration( - color: const Color(0xFFEDF6F5), - borderRadius: BorderRadius.circular(10), - ), - padding: const EdgeInsets.all(20), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - "Enquiry Details", - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.w700, - ), - ), - SizedBox(height: 10), - LayoutBuilder( - builder: (context, constraints) { - final isMobile = constraints.maxWidth < 600; // breakpoint - - if (isMobile) { - // 📱 MOBILE → Column layout - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - registernumber(context), - const SizedBox(height: 16), - insurer(context), - const SizedBox(height: 16), - buildIdv(context), - const SizedBox(height: 16), - buildPremiumAmnt(context), - const SizedBox(height: 16), - buildPlanType(context), - const SizedBox(height: 16), - Wrap( - spacing: 12, - runSpacing: 8, - children: [ - _downloadButton("Download RC"), - _downloadButton("Download ID Proof"), - _downloadButton("Download Previous Policy"), - _downloadButton("Quotation Document"), - ], - ), - ], - ); - } else { - // 💻 WEB → Row layout - return Column( - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Expanded(flex: 2, child: registernumber(context)), - const SizedBox(width: 20), - Expanded(flex: 2, child: insurer(context)), - const SizedBox(width: 20), - Expanded(flex: 2, child: buildIdv(context)), - const SizedBox(width: 20), - Expanded(flex: 2, child: buildPremiumAmnt(context)), - const SizedBox(width: 20), - Expanded(flex: 2, child: buildPlanType(context)), - const SizedBox(width: 20), - ], - ), - const SizedBox(height: 20), - Row( - mainAxisAlignment: MainAxisAlignment.start, - children: [ - _downloadButton("Download RC"), - const SizedBox(width: 20), - _downloadButton("Download ID Proof"), - const SizedBox(width: 20), - _downloadButton("Download Previous Policy"), - const SizedBox(width: 20), - _downloadButton("Quotation Document"), - - ], - ), - ], - ); - } - }, - ), - ], - ), - ), - - - const SizedBox(height: 20), - - //2ND SECTION - Container( - // color: Colors.green, - decoration: BoxDecoration( - color: Color(0xffEDF6F5), - borderRadius: BorderRadius.circular(10.0), - ), - width: MediaQuery.of(context).size.width, - // height: MediaQuery.of(context).size.height * 0.8, - padding: EdgeInsets.all(26.0), - child: Column( - children: [ - - - SingleChildScrollView(child: buildFormFields(context)), - - const SizedBox(height: 20), - - //confirm button - Row( - mainAxisAlignment: MainAxisAlignment.end, - children: [ - GestureDetector( - onTap: () { - // handleSave(); - }, - child: Container( - padding: EdgeInsets.symmetric( - horizontal: 45.0, - vertical: 8, - ), - - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(8.0), - color: Color(0xFF425B5B), - ), - child: Text( - 'Sumbit', - style: TextStyle(color: Colors.white), - ), - ), - ), - ], - ), - ], - ), - ), - - const SizedBox(height: 20), - - - - ], - ), + const SizedBox(height: 20), + ], ), ), - ); + ), + ); } Widget _readonlyField(String label, String value) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text(label, style: GoogleFonts.inter(fontSize: 13, fontWeight: FontWeight.w600)), + Text( + label, + style: GoogleFonts.inter(fontSize: 13, fontWeight: FontWeight.w600), + ), const SizedBox(height: 6), Container( padding: const EdgeInsets.symmetric(horizontal: 60, vertical: 14), @@ -372,6 +728,161 @@ class PolicyScreenState extends ConsumerState { ); } + Widget buildDocRC(BuildContext context, bool isMobile) { + return GestureDetector( + onTap: () { + // print("Upload $idget.id}{w"); + final selectedId = selectedEnquiryId; + // final path = + // 'api/agent/downloadAgentIncentiveFile?id=$selectedId'; + final path = + 'api/enquiry/downloadEnquiryFile?enquiry_id=$selectedId&file_type=rc'; + apiService.getPdfDownload(path, selectedId); + }, + child: Container( + padding: const EdgeInsets.all(8), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(5), + color: const Color(0xFF425B5B), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const Text( + "Download RC", + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.w200, + color: Colors.white, + ), + ), + ?isMobile ? null : SizedBox(width: 15), + // isMobile ? const Spacer() : const SizedBox(width: 15), + const Icon( + Icons.file_download_outlined, + size: 16, + color: Colors.white, + ), + ], + ), + ), + ); + } + + Widget buildDocIdProof(BuildContext context, bool isMobile) { + return GestureDetector( + onTap: () { + print("Upload $selectedEnquiryId"); + final selectedId = selectedEnquiryId; + // final path = + // 'api/agent/downloadAgentIncentiveFile?id=$selectedId'; + final path = + 'api/enquiry/downloadEnquiryFile?enquiry_id=$selectedId&file_type=id_proof'; + apiService.getPdfDownload(path, selectedId); + }, + child: Container( + padding: const EdgeInsets.all(8), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(5), + color: const Color(0xFF425B5B), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + "Download ID Proof", + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.w200, + color: Colors.white, + ), + ), + ?isMobile ? null : SizedBox(width: 15), + // const SizedBox(width: 15), + Icon(Icons.file_download_outlined, size: 16, color: Colors.white), + ], + ), + ), + ); + } + + Widget buildDocPrevPolicy(BuildContext context, bool isMobile) { + return GestureDetector( + onTap: () { + print("Upload $selectedEnquiryId"); + final selectedId = selectedEnquiryId; + // final path = + // 'api/agent/downloadAgentIncentiveFile?id=$selectedId'; + final path = + 'api/enquiry/downloadEnquiryFile?enquiry_id=$selectedId&file_type=previous_policy'; + apiService.getPdfDownload(path, selectedId); + }, + child: Container( + padding: const EdgeInsets.all(8), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(5), + color: const Color(0xFF425B5B), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + "Download Previous Policy", + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.w200, + color: Colors.white, + ), + ), + ?isMobile ? null : SizedBox(width: 15), + // isMobile ? const Spacer() : const SizedBox(width: 15), + Icon(Icons.file_download_outlined, size: 16, color: Colors.white), + ], + ), + ), + ); + } + + Widget buildDocQuotation(BuildContext context, bool isMobile) { + return GestureDetector( + onTap: () { + print("Upload $selectedEnquiryId"); + final selectedId = acceptedQuotationId; + // final path = + // 'api/agent/downloadAgentIncentiveFile?id=$selectedId'; + // final path = + // 'api/enquiry/downloadEnquiryFile?enquiry_id=$selectedId&file_type=previous_policy'; + final path = + 'api/quotation/downloadAdditionalUploadedFile?id=$acceptedQuotationId'; + + apiService.getPdfDownload(path, selectedId); + }, + child: Container( + padding: const EdgeInsets.all(8), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(5), + color: const Color(0xFF425B5B), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + "Quotation Documents", + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.w200, + color: Colors.white, + ), + ), + ?isMobile ? null : SizedBox(width: 15), + // isMobile ? const Spacer() : const SizedBox(width: 15), + Icon(Icons.file_download_outlined, size: 16, color: Colors.white), + ], + ), + ), + ); + } + Widget buildFormFields(BuildContext context) { return Form( key: _formKey, @@ -397,8 +908,8 @@ class PolicyScreenState extends ConsumerState { _buildResponsiveRow( context, policyenddate(context), - buildDocuments(context), - buildDocuments(context), + buildUploadPolicyPdf(context), + buildUploadPolicyReceipt(context), ), // if (!ResponsiveLayout.isMobile(context)) const SizedBox(height: 10), @@ -413,12 +924,11 @@ class PolicyScreenState extends ConsumerState { } Widget _buildResponsiveRow( - BuildContext context, - Widget first, - Widget second, - Widget third, - ) - { + BuildContext context, + Widget first, + Widget second, + Widget third, + ) { if (ResponsiveLayout.isMobile(context)) { // Stack vertically return Column(children: [first, const SizedBox(height: 2), second]); @@ -464,227 +974,386 @@ class PolicyScreenState extends ConsumerState { Widget registernumber(BuildContext context) { return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text('Register Number',style:_textStyle ,), - SizedBox(height: 10,), - ThemedFormField( - controller: controllers['mobile']!, - validator: (value) => Validators.phone(value, "phNumber"), - readOnly:true, - txtwidth: ResponsiveLayout.isMobile(context) - ? null - : MediaQuery.of(context).size.width * 0.26, - ), - ] + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('Register Number*', style: _textStyle), + SizedBox(height: 10), + ThemedFormField( + controller: controllers['regNum']!, + // validator: (value) => Validators.phone(value, "phNumber"), + readOnly: true, + txtwidth: ResponsiveLayout.isMobile(context) + ? null + : MediaQuery.of(context).size.width * 0.26, + ), + ], ); } + Widget insurer(BuildContext context) { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text('Insurer',style:_textStyle ,), - SizedBox(height: 10,), - ThemedFormField( - controller: controllers['mobile']!, - validator: (value) => Validators.phone(value, "phNumber"), - readOnly:true, - txtwidth: ResponsiveLayout.isMobile(context) - ? null - : MediaQuery.of(context).size.width * 0.26, - ), - ] - ); - } - Widget buildIdv(BuildContext context) { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text('IDV',style:_textStyle ,), - SizedBox(height: 10,), - ThemedFormField( - controller: controllers['mobile']!, - validator: (value) => Validators.phone(value, "phNumber"), - readOnly:true, - txtwidth: ResponsiveLayout.isMobile(context) - ? null - : MediaQuery.of(context).size.width * 0.26, - ), - ] - ); - } - Widget buildPremiumAmnt(BuildContext context) { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text('Premium Amount',style:_textStyle ,), - SizedBox(height: 10,), - ThemedFormField( - controller: controllers['mobile']!, - validator: (value) => Validators.phone(value, "phNumber"), - readOnly:true, - txtwidth: ResponsiveLayout.isMobile(context) - ? null - : MediaQuery.of(context).size.width * 0.26, - ), - ] - ); - } - Widget buildPlanType(BuildContext context) { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text('Plan Type',style:_textStyle ,), - SizedBox(height: 10,), - ThemedFormField( - controller: controllers['mobile']!, - validator: (value) => Validators.phone(value, "phNumber"), - readOnly:true, - txtwidth: ResponsiveLayout.isMobile(context) - ? null - : MediaQuery.of(context).size.width * 0.26, - ), - ] - ); - } - - - - - Widget Insuredname(BuildContext context) { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text('Insured Name',style:_textStyle ,), - SizedBox(height: 10,), - ThemedFormField( - controller: controllers['mobile']!, - validator: (value) => Validators.phone(value, "phNumber"), - txtwidth: ResponsiveLayout.isMobile(context) - ? null - : MediaQuery.of(context).size.width * 0.26, - ), - ] - ); - } - Widget premiumamount(BuildContext context) { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text('Premium Amount',style:_textStyle ,), - SizedBox(height: 10,), - ThemedFormField( - controller: controllers['mobile']!, - validator: (value) => Validators.phone(value, "phNumber"), - txtwidth: ResponsiveLayout.isMobile(context) - ? null - : MediaQuery.of(context).size.width * 0.26, - ), - ] - ); - } - Widget policynumber(BuildContext context) { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text('Policy Number',style:_textStyle ,), - SizedBox(height: 10,), - ThemedFormField( - controller: controllers['mobile']!, - validator: (value) => Validators.phone(value, "phNumber"), - txtwidth: ResponsiveLayout.isMobile(context) - ? null - : MediaQuery.of(context).size.width * 0.26, - ), - ] - ); - } - Widget Paymentmode(BuildContext context) { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text('Payment Mode',style:_textStyle ,), - SizedBox(height: 10,), - ThemedFormField( - controller: controllers['mobile']!, - validator: (value) => Validators.phone(value, "phNumber"), - txtwidth: ResponsiveLayout.isMobile(context) - ? null - : MediaQuery.of(context).size.width * 0.26, - ), - ] - ); - } - Widget policyissuedate(BuildContext context) { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text('Policy Issue Date',style:_textStyle ,), - SizedBox(height: 10,), - ThemedFormField( - controller: controllers['mobile']!, - validator: (value) => Validators.phone(value, "phNumber"), - txtwidth: ResponsiveLayout.isMobile(context) - ? null - : MediaQuery.of(context).size.width * 0.26, - ), - ] - ); - } - Widget policystartdate(BuildContext context) { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text('Policy Start Date',style:_textStyle ,), - SizedBox(height: 10,), - ThemedFormField( - controller: controllers['mobile']!, - validator: (value) => Validators.phone(value, "phNumber"), - txtwidth: ResponsiveLayout.isMobile(context) - ? null - : MediaQuery.of(context).size.width * 0.26, - ), - ] - ); - } - Widget policyenddate(BuildContext context) { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text('Policy End Date',style:_textStyle ,), - SizedBox(height: 10,), - ThemedFormField( - controller: controllers['mobile']!, - validator: (value) => Validators.phone(value, "phNumber"), - txtwidth: ResponsiveLayout.isMobile(context) - ? null - : MediaQuery.of(context).size.width * 0.26, - ), - ] - ); - } - Widget buildDocuments(BuildContext context) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text( - 'Quotation Documents ', - style: _textStyle, + Text('Insurer*', style: _textStyle), + SizedBox(height: 10), + ThemedFormField( + controller: controllers['insurer']!, + readOnly: true, + txtwidth: ResponsiveLayout.isMobile(context) + ? null + : MediaQuery.of(context).size.width * 0.26, ), - const SizedBox(height: 10), - buildResponsiveUploadField( - hintText: selectedFileNames, - onFileSelected: (fileName, file) { - print("File picked: $fileName"); - setState(() { - docUploadedFile = file; - }); + ], + ); + } + + Widget buildIdv(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('IDV *', style: _textStyle), + SizedBox(height: 10), + ThemedFormField( + controller: controllers['idv']!, + readOnly: true, + txtwidth: ResponsiveLayout.isMobile(context) + ? null + : MediaQuery.of(context).size.width * 0.26, + ), + ], + ); + } + + Widget buildPremiumAmnt(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('Premium Amount *', style: _textStyle), + SizedBox(height: 10), + ThemedFormField( + controller: controllers['premium_amount']!, + readOnly: true, + txtwidth: ResponsiveLayout.isMobile(context) + ? null + : MediaQuery.of(context).size.width * 0.26, + ), + ], + ); + } + + Widget buildPlanType(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('Plan Type *', style: _textStyle), + SizedBox(height: 10), + ThemedFormField( + controller: controllers['planType']!, + readOnly: true, + txtwidth: ResponsiveLayout.isMobile(context) + ? null + : MediaQuery.of(context).size.width * 0.26, + ), + ], + ); + } + + Widget Insuredname(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('Insured Name *', style: _textStyle), + SizedBox(height: 10), + ThemedFormField( + controller: controllers['policyInsName']!, + validator: (value) => + Validators.requiredField(value, "policyInsName"), + txtwidth: ResponsiveLayout.isMobile(context) + ? null + : MediaQuery.of(context).size.width * 0.26, + ), + ], + ); + } + + Widget premiumamount(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('Premium Amount *', style: _textStyle), + SizedBox(height: 10), + ThemedFormField( + controller: controllers['policyPremiumAmount']!, + validator: (value) => + Validators.requiredField(value, "policyPremiumAmount"), + txtwidth: ResponsiveLayout.isMobile(context) + ? null + : MediaQuery.of(context).size.width * 0.26, + ), + ], + ); + } + + Widget policynumber(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('Policy Number *', style: _textStyle), + SizedBox(height: 10), + ThemedFormField( + controller: controllers['policyNumber']!, + validator: (value) => Validators.requiredField(value, "policyNumber"), + txtwidth: ResponsiveLayout.isMobile(context) + ? null + : MediaQuery.of(context).size.width * 0.26, + ), + ], + ); + } + + Widget Paymentmode(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('Payment Mode *', style: _textStyle), + SizedBox(height: 10), + ThemedFormField( + controller: controllers['policyPaymentMode']!, + validator: (value) => + Validators.requiredField(value, "policyPaymentMode"), + txtwidth: ResponsiveLayout.isMobile(context) + ? null + : MediaQuery.of(context).size.width * 0.26, + ), + ], + ); + } + + Widget policyissuedate(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('Policy Issue Date *', style: _textStyle), + SizedBox(height: 10), + ThemedDateField( + hintText: "Select Date", + txtwidth: ResponsiveLayout.isMobile(context) + ? null + : MediaQuery.of(context).size.width * 0.26, + txtheight: 50, + // backgroundColor: const Color(0xFFECECEC), + validator: (value) => Validators.requiredField(value, "date"), + controller: controllers['issueDate']!, + onDateSelected: (date) { + print("Picked Date: $date"); + + controllers['issueDate']?.text = DateFormat( + 'dd-MM-yyyy', + ).format(date); + // controllers['date']?.text = date as String; }, ), ], ); } + Widget policystartdate(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('Policy Start Date *', style: _textStyle), + SizedBox(height: 10), + ThemedDateField( + hintText: "Select Date", + txtwidth: ResponsiveLayout.isMobile(context) + ? null + : MediaQuery.of(context).size.width * 0.26, + txtheight: 50, + // backgroundColor: const Color(0xFFECECEC), + validator: (value) => Validators.requiredField(value, "date"), + controller: controllers['startDate']!, + onDateSelected: (date) { + print("Picked Date: $date"); + + controllers['startDate']?.text = DateFormat( + 'dd-MM-yyyy', + ).format(date); + // controllers['date']?.text = date as String; + }, + ), + ], + ); + } + + Widget policyenddate(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('Policy End Date *', style: _textStyle), + SizedBox(height: 10), + ThemedDateField( + hintText: "Select Date", + txtwidth: ResponsiveLayout.isMobile(context) + ? null + : MediaQuery.of(context).size.width * 0.26, + txtheight: 50, + // backgroundColor: const Color(0xFFECECEC), + validator: (value) => Validators.requiredField(value, "date"), + controller: controllers['endDate']!, + onDateSelected: (date) { + print("Picked Date: $date"); + controllers['endDate']?.text = DateFormat( + 'dd-MM-yyyy', + ).format(date); + // controllers['date']?.text = date as String; + }, + ), + ], + ); + } + + Widget buildUploadPolicyPdf(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('Upload Policy PDF * ', style: _textStyle), + const SizedBox(height: 10), + Column( + mainAxisAlignment: MainAxisAlignment.end, + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + ThemedUploadField( + hintText: selectedPDFFileNames ?? "Upload Document", + txtwidth: ResponsiveLayout.isMobile(context) + ? null + : MediaQuery.of(context).size.width * 0.26, + // backgroundColor: Color(0xFFEDF6F5), + onFileSelected: (fileName, file) { + print("Picked file: $fileName (${file.size} bytes)"); + setState(() { + docPDFUploadedFile = file; + }); + }, + ), + const SizedBox(height: 5), + + if (docUploadedPDFFileUrlFromApi != null) + Container( + // color: Colors.white, + child: Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + GestureDetector( + onTap: () => apiService.downloadFile( + apiUrl: + 'api/policy/downloadPolicyFile?policy_id=$choosedPolcyId&file_type=policy_pdf', + apiId: choosedPolcyId, + localFile: docPDFUploadedFile, + fileName: selectedPDFFileNames, + ), + + child: Container( + padding: const EdgeInsets.all(5), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(5), + color: Color(0xFF425B5B), + // color: Colors.green.shade300, + ), + child: Row( + children: const [ + Text( + "Download", + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.w200, + color: Colors.white, + ), + ), + SizedBox(width: 5), + Icon(Icons.download, size: 13, color: Colors.white), + ], + ), + ), + ), + ], + ), + ), + ], + ), + ], + ); + } + + Widget buildUploadPolicyReceipt(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('Upload Policy Receipt *', style: _textStyle), + const SizedBox(height: 10), + Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + ThemedUploadField( + hintText: selectedFileNames ?? "Upload Document", + txtwidth: ResponsiveLayout.isMobile(context) + ? null + : MediaQuery.of(context).size.width * 0.26, + // backgroundColor: Color(0xFFEDF6F5), + onFileSelected: (fileName, file) { + print("Picked file: $fileName (${file.size} bytes)"); + setState(() { + docUploadedFile = file; + }); + }, + ), + const SizedBox(height: 5), + + if (docUploadedFileUrlFromApi != null) + Container( + // color: Colors.white, + child: Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + GestureDetector( + onTap: () => apiService.downloadFile( + apiUrl: + 'api/policy/downloadPolicyFile?policy_id=$choosedPolcyId&file_type=policy_payment_receipt', + apiId: choosedPolcyId, + localFile: docUploadedFile, + fileName: selectedFileNames, + ), + child: Container( + padding: const EdgeInsets.all(5), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(5), + color: Color(0xFF425B5B), + // color: Colors.green.shade300, + ), + child: Row( + children: const [ + Text( + "Download", + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.w200, + color: Colors.white, + ), + ), + SizedBox(width: 5), + Icon(Icons.download, size: 13, color: Colors.white), + ], + ), + ), + ), + ], + ), + ), + ], + ), + ], + ); + } Widget buildResponsiveUploadField({ String? label, // 🔹 make optional @@ -703,7 +1372,8 @@ class PolicyScreenState extends ConsumerState { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - if (label != null && label.isNotEmpty) ...[ // ✅ only render if label is given + if (label != null && label.isNotEmpty) ...[ + // ✅ only render if label is given Text(label, style: _textStyle), const SizedBox(height: 8), ], @@ -750,12 +1420,8 @@ class PolicyScreenState extends ConsumerState { } } - - static const _textStyle = TextStyle( fontSize: 14, fontWeight: FontWeight.w600, ); - - } diff --git a/lib/presentation/screens/staff/policy/policy_list.dart b/lib/presentation/screens/staff/policy/policy_list.dart index 1775f04..111b236 100644 --- a/lib/presentation/screens/staff/policy/policy_list.dart +++ b/lib/presentation/screens/staff/policy/policy_list.dart @@ -4,17 +4,18 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:go_router/go_router.dart'; import 'package:intl/intl.dart'; import 'package:nhance_partner/presentation/providers/userRoleProvider.dart'; +import 'package:nhance_partner/presentation/screens/staff/assignStaff.dart'; import '../../../../core/routing/routes.dart'; import '../../../../core/services/api_service.dart'; import '../../../../data/utils/Pagination.dart'; import '../../../layouts/main_layout.dart'; import '../../../layouts/responsive_layout.dart'; import '../../../providers/manager_provider.dart'; +import '../../../providers/quotation_staff_proivder.dart'; import '../../../themes/indicators/export_btn.dart'; import '../../../themes/indicators/search_field_theme.dart'; import '../../../widgets/custom_action_popup.dart'; - class policylist extends ConsumerStatefulWidget { const policylist({super.key}); @override @@ -31,6 +32,7 @@ class policylistState extends ConsumerState { List> originalData = []; List> filteredData = []; bool isLoading = false; + dynamic roleId; @override void initState() { @@ -40,7 +42,7 @@ class policylistState extends ConsumerState { Future.microtask(() { final id = ref.read(managerIdProvider); - final roleId = ref.read(userRoleProvider); + roleId = ref.read(userRoleProvider); print("ENQroleId - $roleId"); if (id != null) { @@ -49,6 +51,15 @@ class policylistState extends ConsumerState { }); } + void refresh() { + final id = ref.read(managerIdProvider); + final roleId = ref.read(userRoleProvider); + + if (id != null) { + getStaffList(id, roleId); + } + } + Future getStaffList(int managerId, role) async { print('getClaimList called MagId - $managerId - $role'); setState(() { @@ -56,7 +67,10 @@ class policylistState extends ConsumerState { }); try { - final response = await apiService.fetchPolicyList(managerId, role); + final response = await apiService.fetchPolicyDataOnlyList( + managerId, + role, + ); if (response['status'] == 'success') { final data = response['data']; @@ -94,6 +108,7 @@ class policylistState extends ConsumerState { final sortedData = [...filteredData] ..sort((a, b) => int.parse(b['id']) - int.parse(a['id'])); + // final sortedData = [...filteredData]; final startIndex = (currentPage - 1) * itemsPerPage; final endIndex = (currentPage * itemsPerPage).clamp(0, sortedData.length); @@ -107,8 +122,8 @@ class policylistState extends ConsumerState { // final isActiveStatus = item['is_active'] == "1" ? "active" : "inactive"; return (item['agent_name'] ?? '').toLowerCase().contains( - query.toLowerCase(), - ) || + query.toLowerCase(), + ) || (item['reg_no'] ?? '').toLowerCase().contains( query.toLowerCase(), ) || @@ -127,10 +142,14 @@ class policylistState extends ConsumerState { (item['payment_mode'] ?? '').toLowerCase().contains( query.toLowerCase(), ) || - (item['updated_on'] ?? '').toLowerCase().contains( + (_formatDate(item['updated_on']) ?? '').toLowerCase().contains( query.toLowerCase(), ) || - (item['policy_number'] ?? '').toLowerCase().contains(query.toLowerCase()); + (item['policy_number'] ?? '').toLowerCase().contains( + query.toLowerCase(), + ) || + (item['status'] ?? '').toLowerCase().contains(query.toLowerCase()); + ; }).toList(); }); } @@ -146,24 +165,121 @@ class policylistState extends ConsumerState { } } - List _buildPopupMenuActions(BuildContext context, dynamic data) { + List _buildPopupMenuActions( + BuildContext context, + dynamic data, + id, + regNum, + ) { return [ - GestureDetector( - onTap: () { - Navigator.pop(context); - print('EDITStaff - ${data['id']}'); - dynamic id = data['id']; - // context.go('/staff/$id'); - }, - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - Icon(Icons.edit_sharp, color: Color(0xFF319718), size: 18), - SizedBox(width: 10), - Text('Edit'), - ], + if (roleId == 'manager') ...[ + Material( + color: Colors.transparent, + child: InkWell( + onTap: () { + Navigator.pop(context); + showDialog( + context: context, + builder: (ctx) => AssignStaffDialog( + enquiryPrimaryId: id, + regNum: regNum, + userId: 1, + onSubmit: (value) { + debugPrint("New assignY: $value"); + refresh(); + }, + ), + ); + }, + hoverColor: Color(0xFFE3F1F0), + splashColor: Color(0xFFE3F1F0), + borderRadius: BorderRadius.circular(6), + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4), + child: Row( + // mainAxisSize: MainAxisSize.min, + children: [ + Image.asset( + "assets/miscellaneous/image_3.png", + // height: 45, + // width: 15, + ), + + SizedBox(width: 10), + Text('Assign Staff'), + ], + ), + ), + ), + ), + ], + + Material( + color: Colors.transparent, + child: InkWell( + onTap: () { + Navigator.pop(context); + ref.read(quotationStaffIdProvider.notifier).state = id; + context.go(AppRoutes.quotation); + }, + hoverColor: Color(0xFFE3F1F0), + splashColor: Color(0xFFE3F1F0), + borderRadius: BorderRadius.circular(6), + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4), + child: Row( + // mainAxisSize: MainAxisSize.min, + children: [ + Image.asset( + "assets/miscellaneous/image_1.png", + height: 15, + width: 15, + ), + + const SizedBox(width: 10), + ((data['status'] == 'Policy Created') || + (data['status'] == 'Quotation Accepted')) + ? const Text('View Quotation') + : const Text('Create Quotation'), + ], + ), + ), ), ), + + if (data['status'] == 'Quotation Accepted' || + data['status'] == 'Policy Created') + Material( + color: Colors.transparent, + child: InkWell( + onTap: () { + Navigator.pop(context); + ref.read(quotationStaffIdProvider.notifier).state = id; + context.go(AppRoutes.policy); + }, + hoverColor: Color(0xFFE3F1F0), + splashColor: Color(0xFFE3F1F0), + borderRadius: BorderRadius.circular(6), + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4), + child: Row( + // mainAxisSize: MainAxisSize.min, + children: [ + Image.asset( + "assets/miscellaneous/image_2.png", + height: 15, + width: 15, + ), + + const SizedBox(width: 10), + data['status'] == 'Policy Created' + ? Text('View Policy') + : Text('Create Policy'), + ], + ), + ), + ), + ), ]; } @@ -299,67 +415,65 @@ class policylistState extends ConsumerState { ), SizedBox(height: 10), if (!ResponsiveLayout.isMobile(context)) - Container( - decoration: BoxDecoration( - color: Color(0xFFEDF6F5), - borderRadius: BorderRadius.circular(6), - ), - padding: const EdgeInsets.symmetric( - vertical: 12, - horizontal: 16, - ), - child: const Row( - children: [ - Expanded( - flex: 2, - child: Text('Reg.No', style: _headerStyle), - ), - Expanded( - flex: 2, - child: Text( - 'Agents', - style: _headerStyle, + Container( + decoration: BoxDecoration( + color: Color(0xFFEDF6F5), + borderRadius: BorderRadius.circular(6), + ), + padding: const EdgeInsets.symmetric( + vertical: 12, + horizontal: 16, + ), + child: const Row( + children: [ + Expanded( + flex: 2, + child: Text('Date', style: _headerStyle), ), - ), - Expanded( - flex: 2, - child: Text('Assigned To', style: _headerStyle), - ), - Expanded( - flex: 2, - child: Text('Insurer', style: _headerStyle), - ), - Expanded( - flex: 2, - child: Text('Insured Name', style: _headerStyle), - ), - Expanded( - flex: 2, - child: Text('Premium', style: _headerStyle), - ), - Expanded( - flex: 2, - child: Text('Payment Mode', style: _headerStyle), - ), - Expanded( - flex: 2, - child: Text('Policy Number', style: _headerStyle), - ), - Expanded( - flex: 2, - child: Text('Date', style: _headerStyle), - ), - Expanded( - flex: 2, - child: Text('Status', style: _headerStyle), - ), - Expanded( - flex: 1, - child: Text('Action', style: _headerStyle), - ), - ], + Expanded( + flex: 2, + child: Text('Reg.No', style: _headerStyle), + ), + Expanded( + flex: 2, + child: Text('Agents', style: _headerStyle), + ), + Expanded( + flex: 2, + child: Text('Assigned To', style: _headerStyle), + ), + Expanded( + flex: 2, + child: Text('Insurer', style: _headerStyle), + ), + Expanded( + flex: 2, + child: Text('Insured Name', style: _headerStyle), + ), + Expanded( + flex: 2, + child: Text('Premium', style: _headerStyle), + ), + Expanded( + flex: 2, + child: Text('Payment Mode', style: _headerStyle), + ), + Expanded( + flex: 2, + child: Text('Policy Number', style: _headerStyle), + ), + + Expanded( + flex: 3, + child: Text('Status', style: _headerStyle), + ), + // Expanded( + // flex: 1, + // child: Text('Action', style: _headerStyle), + // ), + ], + ), ), - ), Expanded(child: _buildDataTable(context)), ], ), @@ -407,7 +521,7 @@ class policylistState extends ConsumerState { return ListView.builder( itemCount: ResponsiveLayout.isMobile(context) ? sortedData - .length // only cards for mobile + .length // only cards for mobile : sortedData.length + 1, // +1 for header in desktop itemBuilder: (context, index) { if (!ResponsiveLayout.isMobile(context) && index == 0) { @@ -416,7 +530,7 @@ class policylistState extends ConsumerState { final startIndex = (currentPage - 1) * itemsPerPage; final item = - sortedData[index - (ResponsiveLayout.isMobile(context) ? 0 : 1)]; + sortedData[index - (ResponsiveLayout.isMobile(context) ? 0 : 1)]; final sno = startIndex + index; return !ResponsiveLayout.isMobile(context) @@ -444,7 +558,19 @@ class policylistState extends ConsumerState { ), child: Row( children: [ - Expanded(flex: 2, child: Text('reg_no' ?? '-', style: _dataBold)), + Expanded( + flex: 2, + child: Text( + _formatDate(item['updated_on']) ?? '-', + style: _dataBold, + softWrap: true, + maxLines: 3, + ), + ), + Expanded( + flex: 2, + child: Text(item['reg_no'] ?? '-', style: _dataBold), + ), Expanded( flex: 2, child: Text( @@ -501,52 +627,50 @@ class policylistState extends ConsumerState { maxLines: 3, ), ), + Expanded( - flex: 2, - child: Text( - item['updated_on'] ?? '-', - style: _dataBold, - softWrap: true, - maxLines: 3, - ), - ), - Expanded( - flex: 2, + flex: 3, child: Text(item['status'] ?? '-', style: _dataBold), ), - Expanded( - flex: 1, - child: Row( - children: [ - PopupMenuButton( - color: Colors.white, - padding: EdgeInsets.zero, - offset: Offset(0, 30), - icon: Icon( - Icons.more_vert, - color: Color(0xFF475569), - size: 14, - ), - itemBuilder: (context) => [ - CustomPopupMenuEntry( - child: Container( - padding: EdgeInsets.symmetric( - horizontal: 8, - vertical: 8, - ), - child: Column( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.center, - children: _buildPopupMenuActions(context, item), - ), - ), - ), - ], - ), - ], - ), - ), + // Expanded( + // flex: 1, + // child: Row( + // children: [ + // PopupMenuButton( + // color: Colors.white, + // padding: EdgeInsets.zero, + // offset: Offset(0, 30), + // icon: Icon( + // Icons.more_vert, + // color: Color(0xFF475569), + // size: 14, + // ), + // itemBuilder: (context) => [ + // CustomPopupMenuEntry( + // child: Container( + // padding: EdgeInsets.symmetric( + // horizontal: 8, + // vertical: 8, + // ), + // child: Column( + // mainAxisSize: MainAxisSize.min, + // mainAxisAlignment: MainAxisAlignment.start, + // crossAxisAlignment: CrossAxisAlignment.start, + // children: _buildPopupMenuActions( + // context, + // item, + // item['id'], + // item['reg_no'], + // ), + // ), + // ), + // ), + // ], + // ), + // ], + // ), + // ), ], ), ); @@ -564,65 +688,84 @@ class policylistState extends ConsumerState { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ + /// Top row: Reg.No + Action menu Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text(item['reg_no'] ?? '-', style: _headerStyle), - PopupMenuButton( - color: Colors.white, - padding: EdgeInsets.zero, - offset: Offset(0, 30), - icon: Icon(Icons.more_vert, color: Color(0xFF475569), size: 14), - itemBuilder: (context) => [ - CustomPopupMenuEntry( - child: Container( - padding: EdgeInsets.symmetric(horizontal: 8, vertical: 8), - child: Column( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.center, - children: _buildPopupMenuActions(context, item), - ), - ), - ), - ], - ), + // PopupMenuButton( + // color: Colors.white, + // padding: EdgeInsets.zero, + // offset: Offset(0, 30), + // icon: Icon(Icons.more_vert, color: Color(0xFF475569), size: 14), + // itemBuilder: (context) => [ + // CustomPopupMenuEntry( + // child: Container( + // padding: EdgeInsets.symmetric(horizontal: 8, vertical: 8), + // child: Column( + // mainAxisSize: MainAxisSize.min, + // children: _buildPopupMenuActions( + // context, + // item, + // item['id'], + // item['reg_no'], + // ), + // ), + // ), + // ), + // ], + // ), ], ), const Divider(color: Color(0xffD9EBE8), thickness: 0.8), - // Company + Vehicle Reg No + /// Company + Status Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - crossAxisAlignment: CrossAxisAlignment.start, children: [ Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, - - mainAxisAlignment: MainAxisAlignment.start, children: [ const Text("Company", style: _cardheaderStyle), - Text( - item['insurer_name'] ?? '-', - style: _cardBodyStyle, - maxLines: 3, - softWrap: true, - ), + Text(item['insurer_name'] ?? '-', style: _cardBodyStyle), ], ), ), - SizedBox(width: 5), + const SizedBox(width: 5), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.start, children: [ const Text("Status", style: _cardheaderStyle), + Text(item['status'] ?? '-', style: _cardBodyStyle), + ], + ), + ), + ], + ), + const SizedBox(height: 15), + + /// Agents + Assigned To + Row( + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text("Agent", style: _cardheaderStyle), + Text(item['agent_name'] ?? '-', style: _cardBodyStyle), + ], + ), + ), + const SizedBox(width: 5), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text("Assigned To", style: _cardheaderStyle), Text( - item['status'] ?? '-', + item['assigned_to_name'] ?? '-', style: _cardBodyStyle, - maxLines: 2, - softWrap: true, ), ], ), @@ -631,23 +774,74 @@ class policylistState extends ConsumerState { ), const SizedBox(height: 15), - // Created On + Remarks + /// Insured Name + Premium Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - const Text("Received Date & Time", style: _cardheaderStyle), + const Text("Insured Name", style: _cardheaderStyle), + Text(item['insured_name'] ?? '-', style: _cardBodyStyle), + ], + ), + ), + const SizedBox(width: 5), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text("Premium", style: _cardheaderStyle), + Text(item['premium_amount'] ?? '-', style: _cardBodyStyle), + ], + ), + ), + ], + ), + const SizedBox(height: 15), + + /// Payment Mode + Policy Number + Row( + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text("Payment Mode", style: _cardheaderStyle), + Text(item['payment_mode'] ?? '-', style: _cardBodyStyle), + ], + ), + ), + const SizedBox(width: 5), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text("Policy Number", style: _cardheaderStyle), + Text(item['policy_number'] ?? '-', style: _cardBodyStyle), + ], + ), + ), + ], + ), + const SizedBox(height: 15), + + /// Date + Remarks + Row( + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text("Date", style: _cardheaderStyle), Text( - _formatDate(item['created_on']) ?? '-', + _formatDate(item['updated_on']) ?? '-', style: _cardBodyStyle, ), ], ), ), - SizedBox(width: 5), + const SizedBox(width: 5), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -657,7 +851,6 @@ class policylistState extends ConsumerState { item['remarks'] ?? '-', style: _cardBodyStyle, maxLines: 3, - softWrap: true, ), ], ), diff --git a/lib/presentation/screens/staff/quotations/createQuotationPopUp.dart b/lib/presentation/screens/staff/quotations/createQuotationPopUp.dart new file mode 100644 index 0000000..f011ff2 --- /dev/null +++ b/lib/presentation/screens/staff/quotations/createQuotationPopUp.dart @@ -0,0 +1,604 @@ +import 'dart:convert'; + +import 'package:dropdown_search/dropdown_search.dart'; +import 'package:file_picker/file_picker.dart'; +import 'package:flutter/material.dart'; +import 'package:google_fonts/google_fonts.dart'; +import 'package:http/http.dart' as http; +import 'package:toastification/toastification.dart'; + +import '../../../../core/config/env.dart'; +import '../../../../core/services/api_service.dart'; +import '../../../../data/services/auth_service.dart'; +import '../../../../data/utils/toastNotification.dart'; +import '../../../../data/utils/validators.dart'; +import '../../../layouts/responsive_layout.dart'; +import '../../../themes/indicators/customizd_file_upload.dart'; +import '../../../themes/indicators/input_field_decoration.dart'; +import '../../../themes/indicators/text_field_theme.dart'; + +// 🔹 Custom Dialog Widget +class createQuotatDialog extends StatefulWidget { + final dynamic userId; + final dynamic managerId; + final dynamic selectedQuotationFrmListId; + final dynamic selectedEnquiryId; + final Map? selectedQuotationFrmListdata; + final void Function(String value) onSubmit; + + const createQuotatDialog({ + super.key, + required this.onSubmit, + required this.userId, + this.selectedQuotationFrmListdata, + this.selectedQuotationFrmListId, + required this.managerId, + required this.selectedEnquiryId, + }); + + @override + State createState() => createQuotatDialogState(); +} + +class createQuotatDialogState extends State { + late ApiService apiService; + String? _token; + + String? selectedFileNames; + + bool isLoading = false; + late TextEditingController controller; + Map controllers = {}; + + final _formKey = GlobalKey(); + + String? docUploadedFileUrlFromApi; + String? selectedId; + PlatformFile? docUploadedFile; + + final GlobalKey>> dropDownKey = + GlobalKey>>(); + + List tabHeader = ['idv', 'premium_Amount']; + + List> getInsuranceTypeData = []; + List> filteredInsuranceData = []; + + String? selectedInsPlanType; + String? selectedEndorsement; + + Map dataDetails() { + final data = { + "enquiry_id": widget.selectedEnquiryId, + "insured_declared_value": controllers["idv"]?.text, + "premium_amount": controllers["premium_Amount"]?.text, + "insurance_plan_type_id": selectedInsPlanType, + // "additional_uploaded_file_name": "extra_doc.pdf", + // "created_by": widget.userId, + "manager_id": widget.managerId, + }; + return data; + } + + @override + void initState() { + super.initState(); + apiService = ApiService(); + for (String field in tabHeader) { + controllers[field] = TextEditingController(); + } + + // 🔹 Init logic here (API calls, token fetch, etc.) + _initializeToken(); + getInsuranceType(); + updateData(); + } + + 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 updateData() { + if (widget.selectedQuotationFrmListId != null && + widget.selectedQuotationFrmListdata!.isNotEmpty) { + print('checkData -- ${widget.selectedQuotationFrmListdata}'); + selectedId = widget.selectedQuotationFrmListId!; + // Pre-fill controllers + controllers["idv"]?.text = + widget.selectedQuotationFrmListdata!['insured_declared_value'] + ?.toString() ?? + ''; + controllers["premium_Amount"]?.text = + widget.selectedQuotationFrmListdata!['premium_amount']?.toString() ?? + ''; + + selectedInsPlanType = widget + .selectedQuotationFrmListdata!['insurance_plan_type_id'] + ?.toString(); + + String? apiDocPath = + widget.selectedQuotationFrmListdata!["additional_uploaded_file_name"]; + if (apiDocPath != null && apiDocPath.isNotEmpty) { + print('apiDocPath - $apiDocPath'); + selectedFileNames = apiDocPath.split('/').last; + print('selectedFileNames - $selectedFileNames'); + docUploadedFileUrlFromApi = apiDocPath; + print('passportFileUrlFromApi - $docUploadedFileUrlFromApi'); + docUploadedFile = null; + } else { + selectedFileNames = null; + docUploadedFile = null; + docUploadedFileUrlFromApi = null; + } + } + } + + 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; + }); + } + } + + void handleDone() { + if (!_formKey.currentState!.validate()) return; + 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!; + + final bool isUpdating = widget.selectedQuotationFrmListId != null; + final id = widget.selectedQuotationFrmListId; // keep nullable + + print('id - $id'); + final uri = Uri.parse( + // isUpdating + // ? 'https://venbait.in/nhance/partner/dev/api/agent/updateAgent' + // : 'https://venbait.in/nhance/partner/dev/api/agent/createAgent', + isUpdating + ? '${Env.apiUrl}quotation/updateQuotation' + : '${Env.apiUrl}quotation/createQuotation', + ); + 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'] = + 'nhance-partner-2025-signature-35468846JRhH551HK'; + + // 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(); + } 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( + 'additional_uploaded_file_name', + docUploadedFile!.bytes!, + filename: docUploadedFile!.name, + ); + request.files.add(multipartFile); + } else if (docUploadedFile!.path != null) { + final multipartFile = await http.MultipartFile.fromPath( + 'additional_uploaded_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}"); + widget.onSubmit('Success'); + Navigator.of(context).pop(); + } else { + final responseBody = jsonDecode(response.body); + dynamic msg = responseBody['data']; + print("❌ Submission failed. Status: ${response.statusCode}"); + print("Body: ${response.body}"); + + showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + title: Text("Agent User Creation Failed"), + content: Text( + msg, + // "There was a problem in creating user. Please try again.", + ), + actions: [ + TextButton( + child: Text("OK"), + onPressed: () { + Navigator.of(context).pop(); + }, + ), + ], + ); + }, + ); + } + } catch (e) { + print("🔥 Error submitting user: $e"); + } + } + + @override + Widget build(BuildContext context) { + return AlertDialog( + backgroundColor: Colors.white, + content: Column( + mainAxisSize: MainAxisSize.min, + children: [ + // Header row + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + widget.selectedQuotationFrmListId != null + ? 'Update Quotation' + : 'Create Quotation', + style: GoogleFonts.inter( + color: const Color(0xFF374141), + fontSize: 18, + fontWeight: FontWeight.bold, + ), + ), + GestureDetector( + onTap: () => Navigator.pop(context), + child: Container( + padding: const EdgeInsets.all(5.0), + decoration: BoxDecoration( + color: const Color(0xFFF1F1F1), + borderRadius: BorderRadius.circular(5.0), + ), + child: const Icon(Icons.close, size: 18), + ), + ), + ], + ), + + const SizedBox(height: 16), + + // 🔹 Switch content dynamically + buildFormFields(context), + ], + ), + actions: [ + GestureDetector( + onTap: () { + handleDone(); + // widget.onSubmit(controller.text.trim()); + // Navigator.of(context).pop(); + }, + child: Container( + padding: const EdgeInsets.symmetric(horizontal: 25.0, vertical: 8), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(8.0), + color: const Color(0xFF425B5B), + ), + child: const Text('Save', style: TextStyle(color: Colors.white)), + ), + ), + ], + ); + } + + Widget buildFormFields(BuildContext context) { + return Form( + key: _formKey, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + buildIdv(context), + buildInsurancePlanType(context), + buildPremiumAmnt(context), + buildDocuments(context), + + // if (!ResponsiveLayout.isMobile(context)) const SizedBox(height: 10), + // _buildResponsiveRow( + // context, + // buildAdditonalDocuments(context), + // buildRemarks(context), + // ), + ], + ), + ); + } + + // ------------------------- Claims Part ----------------------------------- + + Widget buildIdv(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('IDV *', style: _textStyle), + SizedBox(height: 10), + ThemedFormField( + controller: controllers['idv']!, + validator: (value) => Validators.doubleNumber(value, "IDV"), + // validator: (value) => Validators.number(value, "IDV"), + backgroundColor: Color(0xFFEDF6F5), + // readOnly: true, + txtwidth: ResponsiveLayout.isMobile(context) + ? null + : MediaQuery.of(context).size.width * 0.26, + ), + ], + ); + } + + Widget buildPremiumAmnt(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('Premium Amount *', style: _textStyle), + SizedBox(height: 10), + ThemedFormField( + controller: controllers['premium_Amount']!, + backgroundColor: Color(0xFFEDF6F5), + validator: (value) => Validators.doubleNumber(value, "PremiumAmount"), + // validator: (value) => Validators.number(value, "PremiumAmount "), + txtwidth: ResponsiveLayout.isMobile(context) + ? null + : MediaQuery.of(context).size.width * 0.26, + ), + ], + ); + } + + 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: 10), + Container( + color: Colors.white, + width: ResponsiveLayout.isMobile(context) + ? null + : MediaQuery.of(context).size.width * 0.26, + // height: 40, + child: DropdownSearch>( + key: dropDownKey, + selectedItem: selectedVehicle.isNotEmpty ? selectedVehicle : null, + items: (filter, infiniteScrollProps) { + return filteredInsuranceData; + }, + + 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 Plan Type", + ).copyWith( + filled: true, + fillColor: Color( + 0xFFEDF6F5, + ), // 👈 makes the dropdown input white + ), + ), + + popupProps: PopupProps.menu( + fit: FlexFit.loose, + constraints: BoxConstraints(maxHeight: 250), + menuProps: MenuProps( + backgroundColor: + Colors.white, // 👈 sets dropdown background to white + ), + showSearchBox: true, + searchFieldProps: TextFieldProps( + decoration: InputDecoration( + filled: true, + fillColor: Colors.white, + hintText: "Search 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 buildDocuments(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('Quotation Documents ', style: _textStyle), + const SizedBox(height: 10), + Column( + mainAxisAlignment: MainAxisAlignment.end, + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + ThemedUploadField( + hintText: selectedFileNames ?? "Upload Document", + txtwidth: ResponsiveLayout.isMobile(context) + ? null + : MediaQuery.of(context).size.width * 0.26, + backgroundColor: Color(0xFFEDF6F5), + onFileSelected: (fileName, file) { + print("Picked file: $fileName (${file.size} bytes)"); + setState(() { + docUploadedFile = file; + }); + }, + ), + const SizedBox(height: 5), + + if (docUploadedFileUrlFromApi != null) + Container( + // color: Colors.white, + child: Row( + children: [ + GestureDetector( + onTap: () => apiService.downloadFile( + apiUrl: + 'api/quotation/downloadAdditionalUploadedFile?id=$selectedId', + apiId: selectedId, + localFile: docUploadedFile, + fileName: selectedFileNames, + ), + child: Container( + padding: const EdgeInsets.all(5), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(5), + color: Color(0xFF425B5B), + // color: Colors.green.shade300, + ), + child: Row( + children: const [ + Text( + "Download", + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.w200, + color: Colors.white, + ), + ), + SizedBox(width: 5), + Icon(Icons.download, size: 13, color: Colors.white), + ], + ), + ), + ), + ], + ), + ), + ], + ), + ], + ); + } + + // ------------------- STyle --------------------------------- + + static final TextStyle _textStyle = TextStyle( + fontSize: 14, + fontWeight: FontWeight.w600, + ); +} diff --git a/lib/presentation/screens/staff/quotations/quotation.dart b/lib/presentation/screens/staff/quotations/quotation.dart index d80161f..69ff367 100644 --- a/lib/presentation/screens/staff/quotations/quotation.dart +++ b/lib/presentation/screens/staff/quotations/quotation.dart @@ -1,6 +1,7 @@ import 'dart:io' as html; import 'package:dropdown_search/dropdown_search.dart'; +import 'package:file_picker/file_picker.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; @@ -16,10 +17,10 @@ import '../../../../data/utils/validators.dart'; import '../../../layouts/main_layout.dart'; import '../../../layouts/responsive_layout.dart'; import '../../../providers/manager_provider.dart'; +import '../../../providers/quotation_staff_proivder.dart'; import '../../../themes/indicators/customizd_file_upload.dart'; import '../../../themes/indicators/text_field_theme.dart'; - - +import 'createQuotationPopUp.dart'; class QuotationScreen extends ConsumerStatefulWidget { const QuotationScreen({super.key}); @@ -38,18 +39,18 @@ class QuotationScreenState extends ConsumerState { late ApiService apiService; final _formKey = GlobalKey(); final GlobalKey>> dropDownKey = - GlobalKey>>(); + GlobalKey>>(); List tabHeader = [ - 'name', - 'email', + 'regNum', + 'insurer', 'mobile', 'code', 'address', 'regNo', ]; late String isActive = "1"; - html.File? docUploadedFile; + PlatformFile? docUploadedFile; // PlatformFile? passportFile; String? selectedFileNames; @@ -61,7 +62,12 @@ class QuotationScreenState extends ConsumerState { Map controllers = {}; String? _token; dynamic userId; + dynamic selectedEnquiryId; dynamic managerId; + dynamic enqQuotation; + dynamic selectedQuotationFrmListId; + + Map? selectedQuotationFrmListData; List> getVehicleTypeData = []; List> filteredVechicleData = []; @@ -71,8 +77,8 @@ class QuotationScreenState extends ConsumerState { Map dataDetails() { final data = { - "name": controllers["name"]?.text, - "email": controllers["email"]?.text, + "regNum": controllers["regNum"]?.text, + "insurer": controllers["insurer"]?.text, "mobile": controllers["mobile"]?.text, "address": controllers["address"]?.text, "agent_code": controllers["code"]?.text, @@ -83,6 +89,7 @@ class QuotationScreenState extends ConsumerState { } List> getQuotationData = []; + late Map enquiryData; List> originalData = []; List> filteredData = []; // bool isLoading = false; @@ -95,34 +102,73 @@ class QuotationScreenState extends ConsumerState { for (String field in tabHeader) { controllers[field] = TextEditingController(); } - // updateData(); - _initializeToken(); - Future.microtask(() { - managerId = ref.watch(managerIdProvider); - userId = ref.watch(userIdProvider); - getQuotationList(managerId); + _initializeToken(); + + Future.microtask(() { + managerId = ref.read(managerIdProvider); // ✅ use read + userId = ref.read(userIdProvider); // ✅ use read + print('QmanagerId - $managerId'); + print('QuserId - $userId'); + enqQuotation = ref.read(quotationStaffIdProvider); // ✅ use read + print('QenqQuotation - $enqQuotation'); + if (enqQuotation != null) { + _loadData(enqQuotation); + } }); } + @override + void dispose() { + // Dispose all TextEditingControllers + for (var controller in controllers.values) { + controller.dispose(); + } + super.dispose(); + } + Future _initializeToken() async { _token = await AuthService.getToken(); print("APISERTOKEN - $_token"); } - Future getQuotationList(int managerId) async { - print('getClaimList called MagId - $managerId'); + void updateEnquiryData(enquiryData) { + setState(() { + selectedEnquiryId = enquiryData['id']; + print('selectedEnquiryId- $selectedEnquiryId'); + controllers["regNum"]?.text = enquiryData['reg_no']; + controllers["insurer"]?.text = enquiryData['insurer_id']; + }); + } + + Future _loadData(enqQuotation) async { + getQuotationList(enqQuotation); + } + + void refresh() { + _loadData(enqQuotation); + } + + Future getQuotationList(enqQuotation) async { + print('getClaimList called enqQuotation- $enqQuotation'); setState(() { isLoading = true; }); try { - final response = await apiService.fetchQuotationList(managerId); + final response = await apiService.findEnqQuotePolicyView(enqQuotation); if (response['status'] == 'success') { print('quoationListData - ${response['data']}'); setState(() { - getQuotationData = List>.from(response['data']); + enquiryData = Map.from(response['data']['enquiry']); + print('enquiryData - ${enquiryData}'); + + updateEnquiryData(enquiryData); + // getQuotationData = List>.from(response['data']); + getQuotationData = List>.from( + response['data']['quotations'], + ); originalData = getQuotationData; filteredData = List.from(originalData); // print('originalData - $getClaimPolicies'); @@ -140,25 +186,82 @@ class QuotationScreenState extends ConsumerState { } } + Color _getStatusColor(String? status) { + switch (status) { + case 'Accepted': + return Colors.green; + case 'Rejected': + return Colors.red; + case 'Pending': + return Colors.yellow; + default: + return Colors.grey; + } + } + + void handleEdit(id, data) { + print("sfd - $id"); + print("sfdewe - $data"); + setState(() { + selectedQuotationFrmListId = id; + selectedQuotationFrmListData = data; + // data = data; + }); + showDialog( + context: context, + builder: (ctx) => createQuotatDialog( + userId: userId, + selectedQuotationFrmListdata: selectedQuotationFrmListData!, + selectedQuotationFrmListId: selectedQuotationFrmListId!, + managerId: managerId, + selectedEnquiryId: selectedEnquiryId, + onSubmit: (value) { + debugPrint("New Endorsement: $value"); + // enrollKey.currentState?.getEndrosmentList(selectedPolicyNumber); + + print(value); + if (value == 'Success') { + refresh(); + } + + // Update claim list or call API + }, + ), + ); + } + @override Widget build(BuildContext context) { - return - MainLayout( - title: 'quotation', - body: Container( + final isMobile = ResponsiveLayout.isMobile(context); + + // final enqQuotation = ref.watch(quotationStaffIdProvider); + // + // if (enqQuotation != null && getQuotationData.isEmpty && !isLoading) { + // // trigger load only once for this value + // Future.microtask(() => _loadData(enqQuotation)); + // } + + // ref.listen(quotationStaffIdProvider, (prev, next) { + // if (prev != next && next != null) { + // _loadData(next); // use the new value, not the old enqQuotation + // } + // }); + + return MainLayout( + title: 'quotation', + body: Container( height: MediaQuery.of(context).size.height, width: MediaQuery.of(context).size.width, child: SingleChildScrollView( scrollDirection: Axis.vertical, child: Column( children: [ - Container( height: 30, width: MediaQuery.of(context).size.width, child: GestureDetector( onTap: () { - context.go(AppRoutes.dashboard); + context.go(AppRoutes.policylist); }, child: Row( crossAxisAlignment: CrossAxisAlignment.center, @@ -184,130 +287,138 @@ class QuotationScreenState extends ConsumerState { SizedBox(height: 10), //1ST SECTION - Container( - decoration: BoxDecoration( - color: const Color(0xFFEDF6F5), - borderRadius: BorderRadius.circular(10), - ), - padding: const EdgeInsets.all(20), - child: LayoutBuilder( - builder: (context, constraints) { - final isMobile = constraints.maxWidth < 600; // breakpoint - - if (isMobile) { - // 📱 MOBILE → Column layout - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - registernumber(context), - const SizedBox(height: 16), - insurer(context), - const SizedBox(height: 16), - Wrap( - spacing: 12, - runSpacing: 8, - children: [ - _downloadButton("Download RC"), - _downloadButton("Download ID Proof"), - _downloadButton("Download Previous Policy"), - ], - ), - ], - ); - } else { - // 💻 WEB → Row layout - return Row( - crossAxisAlignment: CrossAxisAlignment.end, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Expanded(flex: 2, child: registernumber(context)), - const SizedBox(width: 20), - Expanded(flex: 2, child: insurer(context)), - const SizedBox(width: 20), - _downloadButton("Download RC"), - const SizedBox(width: 20), - _downloadButton("Download ID Proof"), - const SizedBox(width: 20), - _downloadButton("Download Previous Policy"), - ], - ); - } - }, - ), - ), - - - const SizedBox(height: 20), - - //2ND SECTION + if (isMobile) ...[ Container( - // color: Colors.green, decoration: BoxDecoration( - color: Color(0xffEDF6F5), - borderRadius: BorderRadius.circular(10.0), + color: const Color(0xFFEDF6F5), + borderRadius: BorderRadius.circular(10), ), - width: MediaQuery.of(context).size.width, - // height: MediaQuery.of(context).size.height * 0.8, - padding: EdgeInsets.all(26.0), + padding: const EdgeInsets.all(20), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ + Text( + "Enquiry Details", + style: GoogleFonts.inter( + fontSize: 16, + fontWeight: FontWeight.w700, + ), + ), + SizedBox(height: 15), + registerNumber(context), + SizedBox(height: 16), + insurer(context), + SizedBox(height: 16), + buildDocRC(context, isMobile), + SizedBox(height: 16), + buildDocIdProof(context, isMobile), + SizedBox(height: 16), + buildDocPrevPolicy(context, isMobile), + ], + ), + ), + ], - SingleChildScrollView(child: buildFormFields(context)), + if (!isMobile) ...[ + Container( + decoration: BoxDecoration( + color: const Color(0xFFEDF6F5), + borderRadius: BorderRadius.circular(10), + ), + padding: const EdgeInsets.all(20), - const SizedBox(height: 20), - - //confirm button + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + "Enquiry Details", + style: GoogleFonts.inter( + fontSize: isMobile ? 14 : 16, + fontWeight: FontWeight.w700, + ), + ), + SizedBox(height: 15), Row( - mainAxisAlignment: MainAxisAlignment.end, + crossAxisAlignment: CrossAxisAlignment.end, children: [ - // GestureDetector( - // onTap: () { - // // handleSave(); - // }, - // child: Container( - // padding: EdgeInsets.symmetric( - // horizontal: 45.0, - // vertical: 8, - // ), - // - // decoration: BoxDecoration( - // borderRadius: BorderRadius.circular(8.0), - // color: Color(0xFF425B5B), - // ), - // child: Text( - // 'Reject', - // style: TextStyle(color: Colors.white), - // ), - // ), - // ), - - //confirm button - GestureDetector( - onTap: () { - // handleSave(); - }, - child: Container( - padding: EdgeInsets.symmetric( - horizontal: 45.0, - vertical: 8, - ), - - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(8.0), - color: Color(0xFF425B5B), - ), - child: Text( - 'Confirm', - style: TextStyle(color: Colors.white), - ), - ), - ), + registerNumber(context), + Spacer(), + insurer(context), + Spacer(), + buildDocRC(context, isMobile), + Spacer(), + buildDocIdProof(context, isMobile), + Spacer(), + buildDocPrevPolicy(context, isMobile), ], ), ], ), ), + ], + const SizedBox(height: 20), + + Container( + padding: EdgeInsets.only(right: 10), + child: Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + GestureDetector( + onTap: () { + // print('Export'); + + showDialog( + context: context, + builder: (ctx) => createQuotatDialog( + userId: userId, + selectedQuotationFrmListdata: + selectedQuotationFrmListData, + selectedQuotationFrmListId: + selectedQuotationFrmListId, + managerId: managerId, + selectedEnquiryId: selectedEnquiryId, + onSubmit: (value) { + debugPrint("New Endorsement: $value"); + // enrollKey.currentState?.getEndrosmentList(selectedPolicyNumber); + + print(value); + if (value == 'Success') { + refresh(); + } + + // Update claim list or call API + }, + ), + ); + }, + child: Container( + padding: EdgeInsets.all(8.0), + decoration: BoxDecoration( + color: Color(0xFF425B5B), + borderRadius: BorderRadius.circular(8.0), + ), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Icon(Icons.add, color: Colors.white), + SizedBox(width: 10), + Text( + 'Create New Quotation', + style: GoogleFonts.inter( + color: Colors.white, + fontWeight: FontWeight.w500, + fontSize: 13, + ), + ), + ], + ), + ), + ), + ], + ), + ), const SizedBox(height: 20), @@ -328,263 +439,199 @@ class QuotationScreenState extends ConsumerState { ], ), ), - - ], ), ), - ), - ); - } - - Widget _readonlyField(String label, String value) { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text(label, style: GoogleFonts.inter(fontSize: 13, fontWeight: FontWeight.w600)), - const SizedBox(height: 6), - Container( - padding: const EdgeInsets.symmetric(horizontal: 60, vertical: 14), - decoration: BoxDecoration( - color: Colors.red, - borderRadius: BorderRadius.circular(6), - border: Border.all(color: Colors.grey.shade300), - ), - child: Text(value, style: GoogleFonts.inter(fontSize: 14)), - ), - ], + ), ); } Widget _downloadButton(String label) { - return ElevatedButton.icon( - style: ElevatedButton.styleFrom( - backgroundColor: const Color(0xFF425B5B), - foregroundColor: Colors.white, - padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 20), - shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(6)), + final isMobile = ResponsiveLayout.isMobile(context); + return GestureDetector( + onTap: () => apiService.downloadFile( + apiUrl: 'api/quotation/downloadAdditionalUploadedFile?id=$selectedId', + apiId: selectedId, + localFile: docUploadedFile, + fileName: selectedFileNames, ), - onPressed: () { - // TODO: file download logic + + child: Container( + width: isMobile ? null : MediaQuery.of(context).size.width * 0.15, + padding: const EdgeInsets.all(10), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(5), + color: const Color(0xFF425B5B), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + label, + style: GoogleFonts.inter( + fontSize: 12, + fontWeight: FontWeight.w500, + color: Colors.white, + ), + ), + + // const SizedBox(width: 5), + const Icon(Icons.download, size: 13, color: Colors.white), + ], + ), + ), + ); + } + + Widget buildDocRC(BuildContext context, bool isMobile) { + return GestureDetector( + onTap: () { + // print("Upload $idget.id}{w"); + final selectedId = selectedEnquiryId; + // final path = + // 'api/agent/downloadAgentIncentiveFile?id=$selectedId'; + final path = + 'api/enquiry/downloadEnquiryFile?enquiry_id=$selectedId&file_type=rc'; + apiService.getPdfDownload(path, selectedId); }, - icon: const Icon(Icons.download, size: 18), - label: Text(label, style: GoogleFonts.inter(fontSize: 13)), - ); - } - - Widget buildFormFields(BuildContext context) { - return Form( - key: _formKey, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - // _buildResponsiveRow( - // context, - // buildName(context), - // buildInsusrer(context), - // ), - if (!ResponsiveLayout.isMobile(context)) const SizedBox(height: 10), - - _buildResponsiveRow( - context, - buildIdv(context), - buildPremiumAmnt(context), - ), - if (!ResponsiveLayout.isMobile(context)) const SizedBox(height: 10), - - _buildResponsiveRow( - context, - buildPlanType(context), - buildDocuments(context), - ), - - // if (!ResponsiveLayout.isMobile(context)) const SizedBox(height: 10), - // _buildResponsiveRow( - // context, - // buildAdditonalDocuments(context), - // buildRemarks(context), - // ), - ], + child: Container( + padding: const EdgeInsets.all(8), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(5), + color: const Color(0xFF425B5B), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const Text( + "Download RC", + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.w200, + color: Colors.white, + ), + ), + ?isMobile ? null : SizedBox(width: 15), + // isMobile ? const Spacer() : const SizedBox(width: 15), + const Icon( + Icons.file_download_outlined, + size: 16, + color: Colors.white, + ), + ], + ), ), ); } - Widget _buildResponsiveRow( - BuildContext context, - Widget first, - Widget second, - ) - { - if (ResponsiveLayout.isMobile(context)) { - // Stack vertically - return Column(children: [first, const SizedBox(height: 2), second]); - } else { - // Place side by side - return Row( - children: [ - Expanded(child: first), - const SizedBox(width: 25), - Expanded(child: second), - ], - ); - } - } - - - - Widget registernumber(BuildContext context) { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text('Register Number',style:_textStyle ,), - SizedBox(height: 10,), - ThemedFormField( - controller: controllers['mobile']!, - validator: (value) => Validators.phone(value, "phNumber"), - readOnly:true, - txtwidth: ResponsiveLayout.isMobile(context) - ? null - : MediaQuery.of(context).size.width * 0.26, - ), - ] + Widget buildDocIdProof(BuildContext context, bool isMobile) { + return GestureDetector( + onTap: () { + print("Upload $selectedEnquiryId"); + final selectedId = selectedEnquiryId; + // final path = + // 'api/agent/downloadAgentIncentiveFile?id=$selectedId'; + final path = + 'api/enquiry/downloadEnquiryFile?enquiry_id=$selectedId&file_type=id_proof'; + apiService.getPdfDownload(path, selectedId); + }, + child: Container( + padding: const EdgeInsets.all(8), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(5), + color: const Color(0xFF425B5B), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + "Download ID Proof", + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.w200, + color: Colors.white, + ), + ), + ?isMobile ? null : SizedBox(width: 15), + // const SizedBox(width: 15), + Icon(Icons.file_download_outlined, size: 16, color: Colors.white), + ], + ), + ), ); } - Widget insurer(BuildContext context) { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text('Insurer',style:_textStyle ,), - SizedBox(height: 10,), - ThemedFormField( - controller: controllers['mobile']!, - validator: (value) => Validators.phone(value, "phNumber"), - readOnly:true, - txtwidth: ResponsiveLayout.isMobile(context) - ? null - : MediaQuery.of(context).size.width * 0.26, - ), - ] + + Widget buildDocPrevPolicy(BuildContext context, bool isMobile) { + return GestureDetector( + onTap: () { + print("Upload $selectedEnquiryId"); + final selectedId = selectedEnquiryId; + // final path = + // 'api/agent/downloadAgentIncentiveFile?id=$selectedId'; + final path = + 'api/enquiry/downloadEnquiryFile?enquiry_id=$selectedId&file_type=previous_policy'; + apiService.getPdfDownload(path, selectedId); + }, + child: Container( + padding: const EdgeInsets.all(8), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(5), + color: const Color(0xFF425B5B), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + "Download Previous Policy", + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.w200, + color: Colors.white, + ), + ), + ?isMobile ? null : SizedBox(width: 15), + // isMobile ? const Spacer() : const SizedBox(width: 15), + Icon(Icons.file_download_outlined, size: 16, color: Colors.white), + ], + ), + ), ); } - Widget buildIdv(BuildContext context) { + + Widget registerNumber(BuildContext context) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text('IDV',style:_textStyle ,), - SizedBox(height: 10,), + Text('Register Number', style: _textStyle), + SizedBox(height: 10), ThemedFormField( - controller: controllers['mobile']!, - validator: (value) => Validators.phone(value, "phNumber"), - readOnly:true, + controller: controllers['regNum']!, + readOnly: true, txtwidth: ResponsiveLayout.isMobile(context) ? null - : MediaQuery.of(context).size.width * 0.26, - ), - ] - ); - } - Widget buildPremiumAmnt(BuildContext context) { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text('Premium Amount',style:_textStyle ,), - SizedBox(height: 10,), - ThemedFormField( - controller: controllers['mobile']!, - validator: (value) => Validators.phone(value, "phNumber"), - txtwidth: ResponsiveLayout.isMobile(context) - ? null - : MediaQuery.of(context).size.width * 0.26, - ), - ] - ); - } - Widget buildPlanType(BuildContext context) { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text('Plan Type',style:_textStyle ,), - SizedBox(height: 10,), - ThemedFormField( - controller: controllers['mobile']!, - validator: (value) => Validators.phone(value, "phNumber"), - txtwidth: ResponsiveLayout.isMobile(context) - ? null - : MediaQuery.of(context).size.width * 0.26, - ), - ] - ); - } - Widget buildDocuments(BuildContext context) { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - 'Quotation Documents ', - style: _textStyle, - ), - const SizedBox(height: 10), - buildResponsiveUploadField( - hintText: selectedFileNames, - onFileSelected: (fileName, file) { - print("File picked: $fileName"); - setState(() { - docUploadedFile = file; - }); - }, + : MediaQuery.of(context).size.width * 0.2, ), ], ); } - - - - - - Widget buildResponsiveUploadField({ - String? label, // 🔹 make optional - required String? hintText, - required void Function(String fileName, dynamic file) onFileSelected, - }) { - final isMobile = ResponsiveLayout.isMobile(context); - - final uploadWidget = ThemedUploadField( - hintText: hintText ?? "Upload Documents", - txtwidth: isMobile ? null : MediaQuery.of(context).size.width * 0.26, - onFileSelected: onFileSelected, + Widget insurer(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('Insurer', style: _textStyle), + SizedBox(height: 10), + ThemedFormField( + controller: controllers['insurer']!, + readOnly: true, + txtwidth: ResponsiveLayout.isMobile(context) + ? null + : MediaQuery.of(context).size.width * 0.2, + ), + ], ); - - if (isMobile) { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - if (label != null && label.isNotEmpty) ...[ // ✅ only render if label is given - Text(label, style: _textStyle), - const SizedBox(height: 8), - ], - uploadWidget, - const SizedBox(height: 16), - ], - ); - } else { - return Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - if (label != null && label.isNotEmpty) ...[ - Expanded(child: Text(label, style: _textStyle)), - const SizedBox(width: 10), - ], - uploadWidget, - ], - ); - } } - - - - Widget buildResponsiveField({required String label, required Widget field}) { final isMobile = ResponsiveLayout.isMobile(context); @@ -610,10 +657,6 @@ class QuotationScreenState extends ConsumerState { } } - - - - Widget buildId(BuildContext context) { return buildResponsiveField( label: "Registration Number *", @@ -646,7 +689,7 @@ class QuotationScreenState extends ConsumerState { physics: NeverScrollableScrollPhysics(), itemCount: ResponsiveLayout.isMobile(context) ? sortedData - .length // only cards for mobile + .length // only cards for mobile : sortedData.length + 1, // +1 for header in desktop itemBuilder: (context, index) { if (!ResponsiveLayout.isMobile(context) && index == 0) { @@ -655,8 +698,8 @@ class QuotationScreenState extends ConsumerState { final startIndex = (currentPage - 1) * itemsPerPage; final item = - sortedData[index - - (ResponsiveLayout.isMobile(context) ? 0 : 1)]; + sortedData[index - + (ResponsiveLayout.isMobile(context) ? 0 : 1)]; final sno = startIndex + index; return !ResponsiveLayout.isMobile(context) @@ -680,14 +723,15 @@ class QuotationScreenState extends ConsumerState { children: [ // Expanded(flex: 3, child: Text(' ', style: _headerStyle)), Expanded( - flex: 3, + flex: 2, child: Text('Registration Number', style: _headerStyle), ), - Expanded(flex: 3, child: Text('Insurer', style: _headerStyle)), - Expanded(flex: 3, child: Text('IDV', style: _headerStyle)), - Expanded(flex: 3, child: Text('Plan Type', style: _headerStyle)), - Expanded(flex: 3, child: Text('Premium Amount', style: _headerStyle)), - // Expanded(flex: 1, child: Text('Status', style: _headerStyle)), + Expanded(flex: 4, child: Text('Insurer', style: _headerStyle)), + Expanded(flex: 2, child: Text('IDV', style: _headerStyle)), + Expanded(flex: 2, child: Text('Plan Type', style: _headerStyle)), + Expanded(flex: 2, child: Text('Premium Amount', style: _headerStyle)), + Expanded(flex: 2, child: Text('Status', style: _headerStyle)), + Expanded(flex: 1, child: Text('', style: _headerStyle)), ], ), ); @@ -696,7 +740,7 @@ class QuotationScreenState extends ConsumerState { Widget _buildDataRow(Map item, sno) { return Container( padding: const EdgeInsets.symmetric(vertical: 6, horizontal: 16), - // margin: const EdgeInsets.only(top: 10), + margin: const EdgeInsets.only(top: 10), decoration: BoxDecoration( color: Colors.white, // color: Color(0xFFE0F7F9), @@ -707,58 +751,77 @@ class QuotationScreenState extends ConsumerState { ), child: Row( children: [ - // ✅ Single-select checkbox - // Expanded( - // flex: 1, - // child: Checkbox( - // value: selectedId == item['id'], // only selected row is checked - // onChanged: (val) { - // setState(() { - // if (val == true) { - // selectedId = item['id']; // select this row - // isquotation = true; - // } else { - // selectedId = null; - // isquotation = false; // unselect - // } - // }); - // print("Selected row id: $selectedId"); - // }, - // ), - // ), - - Expanded(flex: 2, child: Text(item['id'] ?? '-', style: _dataBold)), Expanded( flex: 2, - child: Text(item['email'] ?? '-', style: _dataBold), + child: Text(item['reg_no'] ?? '-', style: _dataBold), + ), + Expanded( + flex: 4, + child: Text(item['insurer_name'] ?? '-', style: _dataBold), ), Expanded( flex: 2, - child: Text(item['mobile'] ?? '-', style: _dataBold), + child: Text( + item['insured_declared_value'] ?? '-', + style: _dataBold, + ), + ), + Expanded( + flex: 2, + child: Text(item['insurance_plan_type'] ?? '-', style: _dataBold), ), Expanded( flex: 2, child: Text(item['premium_amount'] ?? '-', style: _dataBold), ), + Expanded( flex: 2, child: Text( - item['premium_amount'] ?? '-', - style: _dataBold, + item['status'] ?? '-', + style: _dataBold.copyWith(color: _getStatusColor(item['status'])), ), ), - // Expanded( - // flex: 1, - // child: Text( - // item['status'] ?? '-', - // style: TextStyle( - // fontSize: 14, - // - // fontWeight: FontWeight.w400, - // color: item['status'] == 'Accepted' ? Colors.green : Colors.red, - // ), - // ), - // ), + + Expanded( + flex: 1, + child: item['status'] == 'Pending' + ? GestureDetector( + onTap: () { + final id = item['id']; + print('RAV - $id'); + handleEdit(id, item); + }, + child: Image.asset( + "assets/miscellaneous/Edit.png", + height: 15, + width: 15, + ), + ) + : Image.asset( + "assets/miscellaneous/Edit_muted.png", + height: 15, + width: 15, + ), + // child: GestureDetector( + // onTap: () { + // final id = item['id']; + // print('RAV - $id'); + // handleEdit(id, item); + // }, + // child: item['status'] == 'Pending' + // ? Image.asset( + // "assets/miscellaneous/Edit.png", + // height: 15, + // width: 15, + // ) + // : Image.asset( + // "assets/miscellaneous/Edit_muted.png", + // height: 15, + // width: 15, + // ), + // ), + ), ], ), ); @@ -766,10 +829,11 @@ class QuotationScreenState extends ConsumerState { Widget _buildDataCard(Map item, int sno) { return Container( - margin: const EdgeInsets.symmetric(vertical: 6, horizontal: 8), + // margin: const EdgeInsets.symmetric(vertical: 6, horizontal: 8), padding: const EdgeInsets.all(12), decoration: BoxDecoration( - color: const Color(0xFFF6FEFD), + color: const Color(0xFFFFFFFF), + // color: const Color(0xFFF6FEFD), borderRadius: BorderRadius.circular(8.0), border: Border.all(color: const Color(0xffD9EBE8)), ), @@ -779,34 +843,89 @@ class QuotationScreenState extends ConsumerState { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - // Company + Vehicle Reg No Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, - crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + // "TN64V3456", + item['reg_no'] ?? '-', + style: _cardheaderStyle, + ), + item['status'] == 'Pending' + ? GestureDetector( + onTap: () { + final id = item['id']; + print('RAV - $id'); + handleEdit(id, item); + }, + child: Image.asset( + "assets/miscellaneous/Edit.png", + height: 15, + width: 15, + ), + ) + : Image.asset( + "assets/miscellaneous/Edit_muted.png", + height: 15, + width: 15, + ), + ], + ), + Row( + // mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, + // flex: 2, + child: Text( + item['insurer_name'] ?? '-', + style: _cardRow1BodyStyle, + ), + ), + ], + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + item['insurance_plan_type'] ?? '-', + style: _cardRow1BodyStyle, + ), - mainAxisAlignment: MainAxisAlignment.start, + Text( + 'IDV: ${item['insured_declared_value']}' ?? '-', + style: _cardRow2BodyStyle, + ), + ], + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + RichText( + text: TextSpan( children: [ - const Text("New India", style: _cardBodyStyle), - const Text("TN64V3456", style: _cardheaderStyle), - const Text("Status: Rejected", style: _cardBodyStyle), + TextSpan( + text: 'Status : ', // key + style: TextStyle( + color: Colors.black, + fontWeight: FontWeight.w600, + fontSize: 11, + ), // key color + ), + TextSpan( + text: '${item['status'] ?? '-'}', // value + style: TextStyle( + color: _getStatusColor(item['status']), + fontWeight: FontWeight.w600, + fontSize: 11, + ), // value color + ), ], ), ), - SizedBox(width: 5), - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.start, - children: [ - const Text("Comprehensive", style: _cardBodyStyle), - const Text("IDV:₹145000", style: _cardBodyStyle), - const Text("₹1000", style: _cardheaderStyle), - ], - ), + + Text( + '${item['premium_amount']}' ?? '-', + style: _cardheaderStyle, ), ], ), @@ -852,4 +971,17 @@ class QuotationScreenState extends ConsumerState { fontWeight: FontWeight.w400, fontSize: 12, ); + + static const _cardRow1BodyStyle = TextStyle( + // color: Color(0xFF545454), + color: Colors.black, + fontWeight: FontWeight.w500, + fontSize: 12, + ); + static const _cardRow2BodyStyle = TextStyle( + color: Color(0xFF545454), + // color: Colors.black, + fontWeight: FontWeight.w500, + fontSize: 12, + ); } diff --git a/lib/presentation/themes/indicators/date_field_theme.dart b/lib/presentation/themes/indicators/date_field_theme.dart index a9b219e..33b8603 100644 --- a/lib/presentation/themes/indicators/date_field_theme.dart +++ b/lib/presentation/themes/indicators/date_field_theme.dart @@ -57,6 +57,7 @@ class _ThemedDateFieldState extends State { } } + @override @override Widget build(BuildContext context) { return FormField( @@ -64,48 +65,66 @@ class _ThemedDateFieldState extends State { initialValue: widget.controller?.text, builder: (field) { final hasError = field.hasError; - return Container( - width: widget.txtwidth ?? MediaQuery.of(context).size.width, - height: widget.txtheight, - child: InkWell( - onTap: () => pickDate(context, field), - borderRadius: BorderRadius.circular(10), - child: Container( - padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 15), - decoration: BoxDecoration( - color: widget.backgroundColor ?? Colors.white, - borderRadius: BorderRadius.circular(10), - border: Border.all( - color: hasError - ? (widget.errorBorderColor ?? Colors.red) - : (widget.borderColor ?? Colors.grey.shade50), - width: 1.5, + + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + InkWell( + onTap: () => pickDate(context, field), + borderRadius: BorderRadius.circular(10), + child: Container( + width: widget.txtwidth ?? MediaQuery.of(context).size.width, + height: widget.txtheight, + padding: const EdgeInsets.symmetric( + vertical: 10, + horizontal: 15, + ), + decoration: BoxDecoration( + color: widget.backgroundColor ?? Colors.white, + borderRadius: BorderRadius.circular(10), + border: Border.all( + color: hasError + ? (widget.errorBorderColor ?? Color(0xFFD83731)) + : (widget.borderColor ?? Colors.grey.shade50), + width: 1, + ), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: Text( + widget.controller?.text.isNotEmpty == true + ? widget.controller!.text + : widget.hintText ?? "Select Date", + style: TextStyle( + fontSize: 13, + color: hasError ? Color(0xFFD83731) : Colors.black, + ), + overflow: TextOverflow.ellipsis, + ), + ), + const Icon( + Icons.calendar_today, + size: 16, + color: Colors.black, + ), + ], ), ), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Expanded( - child: Text( - widget.controller?.text.isNotEmpty == true - ? widget.controller!.text - : widget.hintText ?? "Select Date", - style: TextStyle( - fontSize: 12, - color: hasError ? Colors.red : Colors.black, - ), - overflow: TextOverflow.ellipsis, - ), - ), - const Icon( - Icons.calendar_today, - size: 16, - color: Colors.black, - ), - ], - ), ), - ), + if (hasError) + Padding( + padding: const EdgeInsets.only(top: 5, left: 5), + child: Text( + field.errorText ?? '', + style: const TextStyle( + color: Color(0xFFD83731), + fontSize: 10, + ), + ), + ), + ], ); }, ); diff --git a/lib/presentation/themes/indicators/export_btn.dart b/lib/presentation/themes/indicators/export_btn.dart index a3322ab..0754c1f 100644 --- a/lib/presentation/themes/indicators/export_btn.dart +++ b/lib/presentation/themes/indicators/export_btn.dart @@ -79,17 +79,23 @@ class ExcelExporter { required String fileName, }) async { try { + // Create Excel and delete default sheet var excel = Excel.createExcel(); - Sheet sheetObject = excel[sheetName]; + if (excel.sheets.containsKey('Sheet1')) { + excel.delete('Sheet1'); + } + + // Create your sheet + Sheet sheetObject = excel[sheetName]; // now this is the only sheet // Add headers sheetObject.appendRow(headers.map((h) => TextCellValue(h)).toList()); // Add data - for (int i = 0; i < data.length; i++) { + for (var rowData in data) { var row = []; for (var key in headers) { - final value = data[i][key]; + final value = rowData[key]; if (value == null) { row.add(TextCellValue('-')); } else if (value is int) { @@ -106,7 +112,7 @@ class ExcelExporter { var bytes = excel.encode()!; if (kIsWeb) { - // ✅ Web: download as file + // Web: download final blob = html.Blob([bytes]); final url = html.Url.createObjectUrlFromBlob(blob); final anchor = html.AnchorElement(href: url) @@ -114,7 +120,7 @@ class ExcelExporter { ..click(); html.Url.revokeObjectUrl(url); } else { - // ✅ Mobile/Desktop: save to file system + // Mobile/Desktop: save file final dir = await getApplicationDocumentsDirectory(); final file = File('${dir.path}/$fileName.xlsx'); await file.writeAsBytes(bytes); @@ -125,3 +131,63 @@ class ExcelExporter { } } } + +// class ExcelExporter { +// static Future exportToExcel({ +// required String sheetName, +// required List> data, +// required List headers, +// required String fileName, +// }) async { +// try { +// // var excel = Excel.createExcel(); +// // Sheet sheetObject = excel[sheetName]; +// +// // Delete default sheet +// var excel = Excel.createExcel(); +// excel.delete('Sheet1'); // remove default +// Sheet sheetObject = excel[sheetName]; +// +// // Add headers +// sheetObject.appendRow(headers.map((h) => TextCellValue(h)).toList()); +// +// // Add data +// for (int i = 0; i < data.length; i++) { +// var row = []; +// for (var key in headers) { +// final value = data[i][key]; +// if (value == null) { +// row.add(TextCellValue('-')); +// } else if (value is int) { +// row.add(IntCellValue(value)); +// } else if (value is double) { +// row.add(DoubleCellValue(value)); +// } else { +// row.add(TextCellValue(value.toString())); +// } +// } +// sheetObject.appendRow(row); +// } +// +// var bytes = excel.encode()!; +// +// if (kIsWeb) { +// // ✅ Web: download as file +// final blob = html.Blob([bytes]); +// final url = html.Url.createObjectUrlFromBlob(blob); +// final anchor = html.AnchorElement(href: url) +// ..setAttribute("download", "$fileName.xlsx") +// ..click(); +// html.Url.revokeObjectUrl(url); +// } else { +// // ✅ Mobile/Desktop: save to file system +// final dir = await getApplicationDocumentsDirectory(); +// final file = File('${dir.path}/$fileName.xlsx'); +// await file.writeAsBytes(bytes); +// await OpenFile.open(file.path); +// } +// } catch (e) { +// print("Error exporting Excel: $e"); +// } +// } +// } diff --git a/lib/presentation/themes/indicators/text_field_theme.dart b/lib/presentation/themes/indicators/text_field_theme.dart index 87c8cfe..8417182 100644 --- a/lib/presentation/themes/indicators/text_field_theme.dart +++ b/lib/presentation/themes/indicators/text_field_theme.dart @@ -103,7 +103,7 @@ class ThemedFormField extends HookWidget { // ✅ dynamic hint color color: const Color(0xFFC3C6CB), ), - // labelStyle: TextStyle(fontSize: 11, color: Colors.red), + labelStyle: TextStyle(fontSize: 12), prefixIconConstraints: const BoxConstraints( maxWidth: 25 + 16 + 10, maxHeight: 25 + (8 * 2), diff --git a/lib/presentation/widgets/drawer_menu.dart b/lib/presentation/widgets/drawer_menu.dart index a5bfed1..db65e2c 100644 --- a/lib/presentation/widgets/drawer_menu.dart +++ b/lib/presentation/widgets/drawer_menu.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:google_fonts/google_fonts.dart'; import '../../core/routing/routes.dart'; import 'package:go_router/go_router.dart'; @@ -22,8 +23,10 @@ class DrawerMenuState extends ConsumerState { _overlayEntry = OverlayEntry( builder: (context) { return Positioned( - left: offset.dx + size.width, - top: offset.dy, + left: offset.dx + size.width - 5, // little spacing + top: offset.dy + (size.height / 2) - 20, + // left: offset.dx + size.width, + // top: offset.dy, child: MouseRegion( onExit: (_) => _hidePopup(), child: Material( @@ -33,125 +36,144 @@ class DrawerMenuState extends ConsumerState { padding: const EdgeInsets.all(10), decoration: BoxDecoration( borderRadius: BorderRadius.circular(6), - color: Colors.white, + color: const Color(0xFFDDF5F1), + // color: Colors.white, ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, - mainAxisSize: MainAxisSize.min, + // mainAxisSize: MainAxisSize.min, children: [ if (key == 'User') ...[ - GestureDetector( - onTap: () { - print("Tappped Agent"); - context.go(AppRoutes.agentLst); - }, - child: const Text( - "Agent", - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.w400, - ), - ), + _buildPopupItem( + label: "Agent", + onTap: () => context.go(AppRoutes.agentLst), ), const SizedBox(height: 5), - GestureDetector( - onTap: () { - print("Bf clicked"); - context.go(AppRoutes.staffLst); - }, - child: const Text( - "Staff", - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.w400, - ), - ), + _buildPopupItem( + label: "Staff", + onTap: () => context.go(AppRoutes.staffLst), ), ], - if (key == 'Reports') ...[ - GestureDetector( - onTap: () { - context.go(AppRoutes.claimlist); - }, - child: const Text( - "Claims", - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.w400, - ), - ), - ), - // const SizedBox(height: 5), - // GestureDetector( - // onTap: () { - // context.go(AppRoutes.enquiryLst); - // }, - // child: const Text( - // "Enquiries", - // style: TextStyle( - // fontSize: 16, - // fontWeight: FontWeight.w400, - // ), - // ), - // ), - const SizedBox(height: 5), - GestureDetector( - onTap: () { - context.go(AppRoutes.endosement); - }, - child: const Text( - "Endorsement", - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.w400, - ), - ), + _buildPopupItem( + label: "Claims", + onTap: () => context.go(AppRoutes.claimlist), ), const SizedBox(height: 5), - GestureDetector( - onTap: () { - context.go(AppRoutes.policylist); - }, - child: const Text( - "Policy", - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.w400, - ), - ), + _buildPopupItem( + label: "Endorsement", + onTap: () => context.go(AppRoutes.endosement), ), - const SizedBox(height: 5), - - GestureDetector( - onTap: () { - context.go(AppRoutes.quotation); - }, - child: const Text( - "Quotation", - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.w400, - ), - ), - ), - - const SizedBox(height: 5), - GestureDetector( - onTap: () { - context.go(AppRoutes.policy); - }, - child: const Text( - "Policy Form", - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.w400, - ), - ), + _buildPopupItem( + label: "Policy", + onTap: () => context.go(AppRoutes.policylist), ), ], + + // if (key == 'User') ...[ + // Row( + // children: [ + // GestureDetector( + // onTap: () { + // print("Tappped Agent"); + // context.go(AppRoutes.agentLst); + // }, + // child: Text("Agent", style: _cardTextStyle), + // ), + // + // Icon( + // Icons.arrow_forward_ios_rounded, + // size: 12, + // color: Colors.black, + // ), + // ], + // ), + // const SizedBox(height: 5), + // Row( + // children: [ + // GestureDetector( + // onTap: () { + // print("Bf clicked"); + // context.go(AppRoutes.staffLst); + // }, + // child: Text("Staff", style: _cardTextStyle), + // ), + // + // Icon( + // Icons.arrow_forward_ios_rounded, + // size: 12, + // color: Colors.black, + // ), + // ], + // ), + // ], + // + // if (key == 'Reports') ...[ + // GestureDetector( + // onTap: () { + // context.go(AppRoutes.claimlist); + // }, + // child: Row( + // children: [ + // Text("Claims", style: _cardTextStyle), + // Icon( + // Icons.arrow_forward_ios_rounded, + // size: 12, + // color: Colors.black, + // ), + // ], + // ), + // ), + // // const SizedBox(height: 5), + // // GestureDetector( + // // onTap: () { + // // context.go(AppRoutes.enquiryLst); + // // }, + // // child: const Text( + // // "Enquiries", + // // style: TextStyle( + // // fontSize: 16, + // // fontWeight: FontWeight.w400, + // // ), + // // ), + // // ), + // const SizedBox(height: 5), + // GestureDetector( + // onTap: () { + // context.go(AppRoutes.endosement); + // }, + // child: Row( + // children: [ + // Text("Endorsement", style: _cardTextStyle), + // + // Icon( + // Icons.arrow_forward_ios_rounded, + // size: 12, + // color: Colors.black, + // ), + // ], + // ), + // ), + // const SizedBox(height: 5), + // GestureDetector( + // onTap: () { + // context.go(AppRoutes.policylist); + // }, + // child: Row( + // children: [ + // Text("Policy", style: _cardTextStyle), + // + // Icon( + // Icons.arrow_forward_ios_rounded, + // size: 12, + // color: Colors.black, + // ), + // ], + // ), + // ), + // ], ], ), ), @@ -183,11 +205,16 @@ class DrawerMenuState extends ConsumerState { padding: EdgeInsets.zero, children: [ SizedBox(height: 30), - DrawerContentWrapper( - child: Image.asset( - "assets/drawer/drawerImg1.png", - height: 25, - width: 25, + GestureDetector( + onTap: () { + context.go(AppRoutes.dashboard); + }, + child: DrawerContentWrapper( + child: Image.asset( + "assets/drawer/drawerImg1.png", + height: 25, + width: 25, + ), ), ), DrawerLabel("Dashboard"), @@ -215,24 +242,16 @@ class DrawerMenuState extends ConsumerState { }, ), DrawerLabel("Reports"), - // DrawerContentWrapper( - // child: IconButton( - // onPressed: () { - // // context.go('/tabEnquiry/tab'); - // ref.read(enquiryIdProvider.notifier).state = 'tab'; - // context.go(AppRoutes.tabEnquiry); - // }, - // icon: Icon(Icons.list_alt_rounded, size: 30, color: Colors.black), - // ), - // ), DrawerContentWrapper( child: Consumer( builder: (context, ref, _) { return IconButton( onPressed: () { - if (roleId == 'manager') { - } else if (roleId == 'agent') { + print('roleI - $roleId'); + if (roleId == 'agent') { context.go(AppRoutes.enquiryLst); + } else { + context.go(AppRoutes.enquiryForStaff); } // ref.read(enquiryIdProvider.notifier).state = null; // context.go(AppRoutes.tabEnquiry); @@ -277,6 +296,15 @@ class DrawerMenuState extends ConsumerState { } } +Widget _buildPopupItem({required String label, required VoidCallback onTap}) { + return _HoverableContainer( + label: label, + onTap: onTap, + normalColor: Color(0xFFDDF5F1), + hoverColor: Colors.white, // change to your desired hover color + ); +} + class DrawerLabel extends StatelessWidget { final String text; const DrawerLabel(this.text, {super.key}); @@ -286,9 +314,71 @@ class DrawerLabel extends StatelessWidget { return Row( mainAxisAlignment: MainAxisAlignment.center, children: [ - Text(text, style: const TextStyle(color: Colors.white, fontSize: 11.5)), + Text( + text, + style: GoogleFonts.inter(color: Colors.white, fontSize: 11.5), + ), SizedBox(height: 20), ], ); } } + +class _HoverableContainer extends StatefulWidget { + final String label; + final VoidCallback onTap; + final Color normalColor; + final Color hoverColor; + + const _HoverableContainer({ + super.key, + required this.label, + required this.onTap, + required this.normalColor, + required this.hoverColor, + }); + + @override + _HoverableContainerState createState() => _HoverableContainerState(); +} + +class _HoverableContainerState extends State<_HoverableContainer> { + bool _isHovered = false; + + @override + Widget build(BuildContext context) { + return MouseRegion( + onEnter: (_) => setState(() => _isHovered = true), + onExit: (_) => setState(() => _isHovered = false), + child: GestureDetector( + onTap: widget.onTap, + child: Container( + decoration: BoxDecoration( + color: _isHovered ? widget.hoverColor : widget.normalColor, + borderRadius: BorderRadius.circular(6), + ), + padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Container( + width: 110, + child: Text(widget.label, style: _cardTextStyle), + ), + const Icon( + Icons.arrow_forward_ios_rounded, + size: 12, + color: Colors.black, + ), + ], + ), + ), + ), + ); + } +} + +TextStyle _cardTextStyle = GoogleFonts.inter( + fontSize: 14, + fontWeight: FontWeight.w500, +); diff --git a/lib/presentation/widgets/mobile_tabs.dart b/lib/presentation/widgets/mobile_tabs.dart index c13890d..ca0b4f4 100644 --- a/lib/presentation/widgets/mobile_tabs.dart +++ b/lib/presentation/widgets/mobile_tabs.dart @@ -1,23 +1,102 @@ import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:go_router/go_router.dart'; +import 'package:google_fonts/google_fonts.dart'; -class MobileTabs extends StatefulWidget { - final ValueChanged onTabChanged; - const MobileTabs({super.key, required this.onTabChanged}); +import '../../core/routing/routes.dart'; +import '../providers/userRoleProvider.dart'; + +class MobileBottomMenu extends ConsumerStatefulWidget { + const MobileBottomMenu({super.key}); @override - State createState() => _MobileTabsState(); + ConsumerState createState() => _MobileBottomMenuState(); } -class _MobileTabsState extends State { +class _MobileBottomMenuState extends ConsumerState { int _currentIndex = 0; + void _showOptionsSheet(List> options) { + showModalBottomSheet( + context: context, + backgroundColor: const Color(0xFFDDF5F1), + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.vertical(top: Radius.circular(16)), + ), + builder: (_) => ListView.separated( + padding: const EdgeInsets.all(16), + shrinkWrap: true, + itemCount: options.length, + separatorBuilder: (_, __) => const SizedBox(height: 8), + itemBuilder: (_, index) { + final item = options[index]; + return ListTile( + title: Text( + item['label'], + style: GoogleFonts.inter( + fontSize: 16, + fontWeight: FontWeight.w500, + ), + ), + trailing: const Icon(Icons.arrow_forward_ios_rounded, size: 16), + onTap: () { + Navigator.pop(context); + context.go(item['route']); + }, + ); + }, + ), + ); + } + @override Widget build(BuildContext context) { + final roleId = ref.watch(userRoleProvider); + return BottomNavigationBar( + type: BottomNavigationBarType.fixed, + backgroundColor: const Color(0xFF425B5B), + selectedItemColor: Colors.white, + unselectedItemColor: Colors.white70, currentIndex: _currentIndex, onTap: (index) { setState(() => _currentIndex = index); - widget.onTabChanged(index); + + switch (index) { + case 0: + context.go(AppRoutes.dashboard); + break; + + case 1: + // Reports tapped → show bottom sheet + _showOptionsSheet([ + {'label': 'Claims', 'route': AppRoutes.claimlist}, + {'label': 'Endorsement', 'route': AppRoutes.endosement}, + {'label': 'Policy', 'route': AppRoutes.policylist}, + ]); + break; + + case 2: + // Enquiry + if (roleId == 'agent') { + context.go(AppRoutes.enquiryLst); + } else { + context.go(AppRoutes.enquiryForStaff); + } + break; + + case 3: + // User options only for manager + if (roleId == 'manager') { + _showOptionsSheet([ + {'label': 'Agent', 'route': AppRoutes.agentLst}, + {'label': 'Staff', 'route': AppRoutes.staffLst}, + ]); + } else { + context.go(AppRoutes.profile); + } + break; + } }, items: [ BottomNavigationBarItem( @@ -28,18 +107,23 @@ class _MobileTabsState extends State { ), label: "Dashboard", ), - const BottomNavigationBarItem( - icon: Icon(Icons.person), - label: "Profile", - ), BottomNavigationBarItem( icon: Image.asset( "assets/drawer/drawerImg2.png", height: 20, width: 20, ), + label: "Reports", + ), + const BottomNavigationBarItem( + icon: Icon(Icons.list_alt_rounded, size: 24, color: Colors.black), label: "Enquiry", ), + + const BottomNavigationBarItem( + icon: Icon(Icons.person_add_alt, size: 24, color: Colors.black), + label: "User", + ), ], ); }