diff --git a/.flutter-plugins-dependencies b/.flutter-plugins-dependencies index 0467880..3e03407 100644 --- a/.flutter-plugins-dependencies +++ b/.flutter-plugins-dependencies @@ -1 +1 @@ -{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"connectivity_plus","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/connectivity_plus-6.1.5/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"device_info_plus","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/device_info_plus-11.5.0/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"file_picker","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/file_picker-10.3.7/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"firebase_auth","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/firebase_auth-6.1.2/","native_build":true,"dependencies":["firebase_core"],"dev_dependency":false},{"name":"firebase_core","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/firebase_core-4.2.1/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"firebase_messaging","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/firebase_messaging-16.0.4/","native_build":true,"dependencies":["firebase_core"],"dev_dependency":false},{"name":"flutter_secure_storage","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/flutter_secure_storage-9.2.4/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"fluttertoast","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/fluttertoast-9.0.0/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"google_sign_in_ios","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/google_sign_in_ios-6.2.1/","shared_darwin_source":true,"native_build":true,"dependencies":[],"dev_dependency":false},{"name":"local_auth_darwin","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/local_auth_darwin-1.6.0/","shared_darwin_source":true,"native_build":true,"dependencies":[],"dev_dependency":false},{"name":"open_file_ios","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/open_file_ios-1.0.3/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"package_info_plus","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/package_info_plus-8.3.1/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"path_provider_foundation","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/path_provider_foundation-2.4.2/","shared_darwin_source":true,"native_build":true,"dependencies":[],"dev_dependency":false},{"name":"pdf_render","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/pdf_render-1.4.12/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"shared_preferences_foundation","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/shared_preferences_foundation-2.5.4/","shared_darwin_source":true,"native_build":true,"dependencies":[],"dev_dependency":false},{"name":"syncfusion_flutter_pdfviewer","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/syncfusion_flutter_pdfviewer-31.1.19/","native_build":true,"dependencies":["device_info_plus"],"dev_dependency":false},{"name":"url_launcher_ios","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/url_launcher_ios-6.3.4/","native_build":true,"dependencies":[],"dev_dependency":false}],"android":[{"name":"connectivity_plus","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/connectivity_plus-6.1.5/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"device_info_plus","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/device_info_plus-11.5.0/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"file_picker","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/file_picker-10.3.7/","native_build":true,"dependencies":["flutter_plugin_android_lifecycle"],"dev_dependency":false},{"name":"firebase_auth","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/firebase_auth-6.1.2/","native_build":true,"dependencies":["firebase_core"],"dev_dependency":false},{"name":"firebase_core","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/firebase_core-4.2.1/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"firebase_messaging","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/firebase_messaging-16.0.4/","native_build":true,"dependencies":["firebase_core"],"dev_dependency":false},{"name":"flutter_plugin_android_lifecycle","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/flutter_plugin_android_lifecycle-2.0.31/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"flutter_secure_storage","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/flutter_secure_storage-9.2.4/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"fluttertoast","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/fluttertoast-9.0.0/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"google_sign_in_android","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/google_sign_in_android-7.2.1/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"local_auth_android","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/local_auth_android-1.0.52/","native_build":true,"dependencies":["flutter_plugin_android_lifecycle"],"dev_dependency":false},{"name":"open_file_android","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/open_file_android-1.0.6/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"package_info_plus","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/package_info_plus-8.3.1/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"path_provider_android","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/path_provider_android-2.2.19/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"pdf_render","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/pdf_render-1.4.12/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"shared_preferences_android","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/shared_preferences_android-2.4.13/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"syncfusion_flutter_pdfviewer","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/syncfusion_flutter_pdfviewer-31.1.19/","native_build":true,"dependencies":["device_info_plus"],"dev_dependency":false},{"name":"url_launcher_android","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/url_launcher_android-6.3.20/","native_build":true,"dependencies":[],"dev_dependency":false}],"macos":[{"name":"connectivity_plus","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/connectivity_plus-6.1.5/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"device_info_plus","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/device_info_plus-11.5.0/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"file_picker","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/file_picker-10.3.7/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"firebase_auth","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/firebase_auth-6.1.2/","native_build":true,"dependencies":["firebase_core"],"dev_dependency":false},{"name":"firebase_core","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/firebase_core-4.2.1/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"firebase_messaging","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/firebase_messaging-16.0.4/","native_build":true,"dependencies":["firebase_core"],"dev_dependency":false},{"name":"flutter_secure_storage_macos","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/flutter_secure_storage_macos-3.1.3/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"google_sign_in_ios","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/google_sign_in_ios-6.2.1/","shared_darwin_source":true,"native_build":true,"dependencies":[],"dev_dependency":false},{"name":"local_auth_darwin","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/local_auth_darwin-1.6.0/","shared_darwin_source":true,"native_build":true,"dependencies":[],"dev_dependency":false},{"name":"open_file_mac","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/open_file_mac-1.0.3/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"package_info_plus","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/package_info_plus-8.3.1/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"path_provider_foundation","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/path_provider_foundation-2.4.2/","shared_darwin_source":true,"native_build":true,"dependencies":[],"dev_dependency":false},{"name":"pdf_render","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/pdf_render-1.4.12/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"shared_preferences_foundation","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/shared_preferences_foundation-2.5.4/","shared_darwin_source":true,"native_build":true,"dependencies":[],"dev_dependency":false},{"name":"syncfusion_pdfviewer_macos","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/syncfusion_pdfviewer_macos-31.1.19/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"url_launcher_macos","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/url_launcher_macos-3.2.3/","native_build":true,"dependencies":[],"dev_dependency":false}],"linux":[{"name":"connectivity_plus","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/connectivity_plus-6.1.5/","native_build":false,"dependencies":[],"dev_dependency":false},{"name":"device_info_plus","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/device_info_plus-11.5.0/","native_build":false,"dependencies":[],"dev_dependency":false},{"name":"file_picker","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/file_picker-10.3.7/","native_build":false,"dependencies":[],"dev_dependency":false},{"name":"flutter_secure_storage_linux","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/flutter_secure_storage_linux-1.2.3/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"open_file_linux","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/open_file_linux-0.0.5/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"package_info_plus","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/package_info_plus-8.3.1/","native_build":false,"dependencies":[],"dev_dependency":false},{"name":"path_provider_linux","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/path_provider_linux-2.2.1/","native_build":false,"dependencies":[],"dev_dependency":false},{"name":"shared_preferences_linux","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/shared_preferences_linux-2.4.1/","native_build":false,"dependencies":["path_provider_linux"],"dev_dependency":false},{"name":"syncfusion_pdfviewer_linux","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/syncfusion_pdfviewer_linux-31.1.19/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"url_launcher_linux","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/url_launcher_linux-3.2.2/","native_build":true,"dependencies":[],"dev_dependency":false}],"windows":[{"name":"connectivity_plus","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/connectivity_plus-6.1.5/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"device_info_plus","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/device_info_plus-11.5.0/","native_build":false,"dependencies":[],"dev_dependency":false},{"name":"file_picker","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/file_picker-10.3.7/","native_build":false,"dependencies":[],"dev_dependency":false},{"name":"firebase_auth","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/firebase_auth-6.1.2/","native_build":true,"dependencies":["firebase_core"],"dev_dependency":false},{"name":"firebase_core","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/firebase_core-4.2.1/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"flutter_secure_storage_windows","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/flutter_secure_storage_windows-3.1.2/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"local_auth_windows","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/local_auth_windows-1.0.11/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"open_file_windows","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/open_file_windows-0.0.3/","native_build":false,"dependencies":[],"dev_dependency":false},{"name":"package_info_plus","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/package_info_plus-8.3.1/","native_build":false,"dependencies":[],"dev_dependency":false},{"name":"path_provider_windows","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/path_provider_windows-2.3.0/","native_build":false,"dependencies":[],"dev_dependency":false},{"name":"shared_preferences_windows","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/shared_preferences_windows-2.4.1/","native_build":false,"dependencies":["path_provider_windows"],"dev_dependency":false},{"name":"syncfusion_pdfviewer_windows","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/syncfusion_pdfviewer_windows-31.1.19/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"url_launcher_windows","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/url_launcher_windows-3.1.5/","native_build":true,"dependencies":[],"dev_dependency":false}],"web":[{"name":"connectivity_plus","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/connectivity_plus-6.1.5/","dependencies":[],"dev_dependency":false},{"name":"device_info_plus","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/device_info_plus-11.5.0/","dependencies":[],"dev_dependency":false},{"name":"file_picker","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/file_picker-10.3.7/","dependencies":[],"dev_dependency":false},{"name":"firebase_auth_web","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/firebase_auth_web-6.1.0/","dependencies":["firebase_core_web"],"dev_dependency":false},{"name":"firebase_core_web","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/firebase_core_web-3.3.0/","dependencies":[],"dev_dependency":false},{"name":"firebase_messaging_web","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/firebase_messaging_web-4.1.0/","dependencies":["firebase_core_web"],"dev_dependency":false},{"name":"flutter_secure_storage_web","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/flutter_secure_storage_web-1.2.1/","dependencies":[],"dev_dependency":false},{"name":"fluttertoast","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/fluttertoast-9.0.0/","dependencies":[],"dev_dependency":false},{"name":"google_sign_in_web","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/google_sign_in_web-1.1.0/","dependencies":[],"dev_dependency":false},{"name":"open_file_web","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/open_file_web-0.0.4/","dependencies":[],"dev_dependency":false},{"name":"package_info_plus","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/package_info_plus-8.3.1/","dependencies":[],"dev_dependency":false},{"name":"pdf_render","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/pdf_render-1.4.12/","dependencies":[],"dev_dependency":false},{"name":"shared_preferences_web","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/shared_preferences_web-2.4.3/","dependencies":[],"dev_dependency":false},{"name":"syncfusion_pdfviewer_web","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/syncfusion_pdfviewer_web-31.1.19/","dependencies":[],"dev_dependency":false},{"name":"url_launcher_web","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/url_launcher_web-2.4.1/","dependencies":[],"dev_dependency":false}]},"dependencyGraph":[{"name":"connectivity_plus","dependencies":[]},{"name":"device_info_plus","dependencies":[]},{"name":"file_picker","dependencies":["flutter_plugin_android_lifecycle"]},{"name":"firebase_auth","dependencies":["firebase_auth_web","firebase_core"]},{"name":"firebase_auth_web","dependencies":["firebase_core","firebase_core_web"]},{"name":"firebase_core","dependencies":["firebase_core_web"]},{"name":"firebase_core_web","dependencies":[]},{"name":"firebase_messaging","dependencies":["firebase_core","firebase_messaging_web"]},{"name":"firebase_messaging_web","dependencies":["firebase_core","firebase_core_web"]},{"name":"flutter_plugin_android_lifecycle","dependencies":[]},{"name":"flutter_secure_storage","dependencies":["flutter_secure_storage_linux","flutter_secure_storage_macos","flutter_secure_storage_web","flutter_secure_storage_windows"]},{"name":"flutter_secure_storage_linux","dependencies":[]},{"name":"flutter_secure_storage_macos","dependencies":[]},{"name":"flutter_secure_storage_web","dependencies":[]},{"name":"flutter_secure_storage_windows","dependencies":["path_provider"]},{"name":"fluttertoast","dependencies":[]},{"name":"google_sign_in","dependencies":["google_sign_in_android","google_sign_in_ios","google_sign_in_web"]},{"name":"google_sign_in_android","dependencies":[]},{"name":"google_sign_in_ios","dependencies":[]},{"name":"google_sign_in_web","dependencies":[]},{"name":"local_auth","dependencies":["local_auth_android","local_auth_darwin","local_auth_windows"]},{"name":"local_auth_android","dependencies":["flutter_plugin_android_lifecycle"]},{"name":"local_auth_darwin","dependencies":[]},{"name":"local_auth_windows","dependencies":[]},{"name":"open_file","dependencies":["open_file_android","open_file_web","open_file_ios","open_file_mac","open_file_windows","open_file_linux"]},{"name":"open_file_android","dependencies":[]},{"name":"open_file_ios","dependencies":[]},{"name":"open_file_linux","dependencies":[]},{"name":"open_file_mac","dependencies":[]},{"name":"open_file_web","dependencies":[]},{"name":"open_file_windows","dependencies":[]},{"name":"package_info_plus","dependencies":[]},{"name":"path_provider","dependencies":["path_provider_android","path_provider_foundation","path_provider_linux","path_provider_windows"]},{"name":"path_provider_android","dependencies":[]},{"name":"path_provider_foundation","dependencies":[]},{"name":"path_provider_linux","dependencies":[]},{"name":"path_provider_windows","dependencies":[]},{"name":"pdf_render","dependencies":[]},{"name":"shared_preferences","dependencies":["shared_preferences_android","shared_preferences_foundation","shared_preferences_linux","shared_preferences_web","shared_preferences_windows"]},{"name":"shared_preferences_android","dependencies":[]},{"name":"shared_preferences_foundation","dependencies":[]},{"name":"shared_preferences_linux","dependencies":["path_provider_linux"]},{"name":"shared_preferences_web","dependencies":[]},{"name":"shared_preferences_windows","dependencies":["path_provider_windows"]},{"name":"syncfusion_flutter_pdfviewer","dependencies":["url_launcher","device_info_plus","syncfusion_pdfviewer_web","syncfusion_pdfviewer_macos","syncfusion_pdfviewer_windows","syncfusion_pdfviewer_linux"]},{"name":"syncfusion_pdfviewer_linux","dependencies":[]},{"name":"syncfusion_pdfviewer_macos","dependencies":[]},{"name":"syncfusion_pdfviewer_web","dependencies":[]},{"name":"syncfusion_pdfviewer_windows","dependencies":[]},{"name":"url_launcher","dependencies":["url_launcher_android","url_launcher_ios","url_launcher_linux","url_launcher_macos","url_launcher_web","url_launcher_windows"]},{"name":"url_launcher_android","dependencies":[]},{"name":"url_launcher_ios","dependencies":[]},{"name":"url_launcher_linux","dependencies":[]},{"name":"url_launcher_macos","dependencies":[]},{"name":"url_launcher_web","dependencies":[]},{"name":"url_launcher_windows","dependencies":[]}],"date_created":"2025-12-15 09:52:48.563323","version":"3.35.7","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":"device_info_plus","path":"/home/venba/.pub-cache/hosted/pub.dev/device_info_plus-11.5.0/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"file_picker","path":"/home/venba/.pub-cache/hosted/pub.dev/file_picker-10.3.7/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"firebase_auth","path":"/home/venba/.pub-cache/hosted/pub.dev/firebase_auth-6.1.2/","native_build":true,"dependencies":["firebase_core"],"dev_dependency":false},{"name":"firebase_core","path":"/home/venba/.pub-cache/hosted/pub.dev/firebase_core-4.2.1/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"firebase_messaging","path":"/home/venba/.pub-cache/hosted/pub.dev/firebase_messaging-16.0.4/","native_build":true,"dependencies":["firebase_core"],"dev_dependency":false},{"name":"flutter_secure_storage","path":"/home/venba/.pub-cache/hosted/pub.dev/flutter_secure_storage-9.2.4/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"fluttertoast","path":"/home/venba/.pub-cache/hosted/pub.dev/fluttertoast-9.0.0/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"google_sign_in_ios","path":"/home/venba/.pub-cache/hosted/pub.dev/google_sign_in_ios-6.2.1/","shared_darwin_source":true,"native_build":true,"dependencies":[],"dev_dependency":false},{"name":"local_auth_darwin","path":"/home/venba/.pub-cache/hosted/pub.dev/local_auth_darwin-1.6.0/","shared_darwin_source":true,"native_build":true,"dependencies":[],"dev_dependency":false},{"name":"open_file_ios","path":"/home/venba/.pub-cache/hosted/pub.dev/open_file_ios-1.0.3/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"package_info_plus","path":"/home/venba/.pub-cache/hosted/pub.dev/package_info_plus-8.3.1/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"path_provider_foundation","path":"/home/venba/.pub-cache/hosted/pub.dev/path_provider_foundation-2.4.2/","shared_darwin_source":true,"native_build":true,"dependencies":[],"dev_dependency":false},{"name":"pdf_render","path":"/home/venba/.pub-cache/hosted/pub.dev/pdf_render-1.4.12/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"shared_preferences_foundation","path":"/home/venba/.pub-cache/hosted/pub.dev/shared_preferences_foundation-2.5.4/","shared_darwin_source":true,"native_build":true,"dependencies":[],"dev_dependency":false},{"name":"syncfusion_flutter_pdfviewer","path":"/home/venba/.pub-cache/hosted/pub.dev/syncfusion_flutter_pdfviewer-31.1.19/","native_build":true,"dependencies":["device_info_plus"],"dev_dependency":false},{"name":"url_launcher_ios","path":"/home/venba/.pub-cache/hosted/pub.dev/url_launcher_ios-6.3.4/","native_build":true,"dependencies":[],"dev_dependency":false}],"android":[{"name":"connectivity_plus","path":"/home/venba/.pub-cache/hosted/pub.dev/connectivity_plus-6.1.5/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"device_info_plus","path":"/home/venba/.pub-cache/hosted/pub.dev/device_info_plus-11.5.0/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"file_picker","path":"/home/venba/.pub-cache/hosted/pub.dev/file_picker-10.3.7/","native_build":true,"dependencies":["flutter_plugin_android_lifecycle"],"dev_dependency":false},{"name":"firebase_auth","path":"/home/venba/.pub-cache/hosted/pub.dev/firebase_auth-6.1.2/","native_build":true,"dependencies":["firebase_core"],"dev_dependency":false},{"name":"firebase_core","path":"/home/venba/.pub-cache/hosted/pub.dev/firebase_core-4.2.1/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"firebase_messaging","path":"/home/venba/.pub-cache/hosted/pub.dev/firebase_messaging-16.0.4/","native_build":true,"dependencies":["firebase_core"],"dev_dependency":false},{"name":"flutter_plugin_android_lifecycle","path":"/home/venba/.pub-cache/hosted/pub.dev/flutter_plugin_android_lifecycle-2.0.31/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"flutter_secure_storage","path":"/home/venba/.pub-cache/hosted/pub.dev/flutter_secure_storage-9.2.4/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"fluttertoast","path":"/home/venba/.pub-cache/hosted/pub.dev/fluttertoast-9.0.0/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"google_sign_in_android","path":"/home/venba/.pub-cache/hosted/pub.dev/google_sign_in_android-7.2.1/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"local_auth_android","path":"/home/venba/.pub-cache/hosted/pub.dev/local_auth_android-1.0.52/","native_build":true,"dependencies":["flutter_plugin_android_lifecycle"],"dev_dependency":false},{"name":"open_file_android","path":"/home/venba/.pub-cache/hosted/pub.dev/open_file_android-1.0.6/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"package_info_plus","path":"/home/venba/.pub-cache/hosted/pub.dev/package_info_plus-8.3.1/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"path_provider_android","path":"/home/venba/.pub-cache/hosted/pub.dev/path_provider_android-2.2.19/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"pdf_render","path":"/home/venba/.pub-cache/hosted/pub.dev/pdf_render-1.4.12/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"shared_preferences_android","path":"/home/venba/.pub-cache/hosted/pub.dev/shared_preferences_android-2.4.13/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"syncfusion_flutter_pdfviewer","path":"/home/venba/.pub-cache/hosted/pub.dev/syncfusion_flutter_pdfviewer-31.1.19/","native_build":true,"dependencies":["device_info_plus"],"dev_dependency":false},{"name":"url_launcher_android","path":"/home/venba/.pub-cache/hosted/pub.dev/url_launcher_android-6.3.20/","native_build":true,"dependencies":[],"dev_dependency":false}],"macos":[{"name":"connectivity_plus","path":"/home/venba/.pub-cache/hosted/pub.dev/connectivity_plus-6.1.5/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"device_info_plus","path":"/home/venba/.pub-cache/hosted/pub.dev/device_info_plus-11.5.0/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"file_picker","path":"/home/venba/.pub-cache/hosted/pub.dev/file_picker-10.3.7/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"firebase_auth","path":"/home/venba/.pub-cache/hosted/pub.dev/firebase_auth-6.1.2/","native_build":true,"dependencies":["firebase_core"],"dev_dependency":false},{"name":"firebase_core","path":"/home/venba/.pub-cache/hosted/pub.dev/firebase_core-4.2.1/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"firebase_messaging","path":"/home/venba/.pub-cache/hosted/pub.dev/firebase_messaging-16.0.4/","native_build":true,"dependencies":["firebase_core"],"dev_dependency":false},{"name":"flutter_secure_storage_macos","path":"/home/venba/.pub-cache/hosted/pub.dev/flutter_secure_storage_macos-3.1.3/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"google_sign_in_ios","path":"/home/venba/.pub-cache/hosted/pub.dev/google_sign_in_ios-6.2.1/","shared_darwin_source":true,"native_build":true,"dependencies":[],"dev_dependency":false},{"name":"local_auth_darwin","path":"/home/venba/.pub-cache/hosted/pub.dev/local_auth_darwin-1.6.0/","shared_darwin_source":true,"native_build":true,"dependencies":[],"dev_dependency":false},{"name":"open_file_mac","path":"/home/venba/.pub-cache/hosted/pub.dev/open_file_mac-1.0.3/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"package_info_plus","path":"/home/venba/.pub-cache/hosted/pub.dev/package_info_plus-8.3.1/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"path_provider_foundation","path":"/home/venba/.pub-cache/hosted/pub.dev/path_provider_foundation-2.4.2/","shared_darwin_source":true,"native_build":true,"dependencies":[],"dev_dependency":false},{"name":"pdf_render","path":"/home/venba/.pub-cache/hosted/pub.dev/pdf_render-1.4.12/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"shared_preferences_foundation","path":"/home/venba/.pub-cache/hosted/pub.dev/shared_preferences_foundation-2.5.4/","shared_darwin_source":true,"native_build":true,"dependencies":[],"dev_dependency":false},{"name":"syncfusion_pdfviewer_macos","path":"/home/venba/.pub-cache/hosted/pub.dev/syncfusion_pdfviewer_macos-31.1.19/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"url_launcher_macos","path":"/home/venba/.pub-cache/hosted/pub.dev/url_launcher_macos-3.2.3/","native_build":true,"dependencies":[],"dev_dependency":false}],"linux":[{"name":"connectivity_plus","path":"/home/venba/.pub-cache/hosted/pub.dev/connectivity_plus-6.1.5/","native_build":false,"dependencies":[],"dev_dependency":false},{"name":"device_info_plus","path":"/home/venba/.pub-cache/hosted/pub.dev/device_info_plus-11.5.0/","native_build":false,"dependencies":[],"dev_dependency":false},{"name":"file_picker","path":"/home/venba/.pub-cache/hosted/pub.dev/file_picker-10.3.7/","native_build":false,"dependencies":[],"dev_dependency":false},{"name":"flutter_secure_storage_linux","path":"/home/venba/.pub-cache/hosted/pub.dev/flutter_secure_storage_linux-1.2.3/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"open_file_linux","path":"/home/venba/.pub-cache/hosted/pub.dev/open_file_linux-0.0.5/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"package_info_plus","path":"/home/venba/.pub-cache/hosted/pub.dev/package_info_plus-8.3.1/","native_build":false,"dependencies":[],"dev_dependency":false},{"name":"path_provider_linux","path":"/home/venba/.pub-cache/hosted/pub.dev/path_provider_linux-2.2.1/","native_build":false,"dependencies":[],"dev_dependency":false},{"name":"shared_preferences_linux","path":"/home/venba/.pub-cache/hosted/pub.dev/shared_preferences_linux-2.4.1/","native_build":false,"dependencies":["path_provider_linux"],"dev_dependency":false},{"name":"syncfusion_pdfviewer_linux","path":"/home/venba/.pub-cache/hosted/pub.dev/syncfusion_pdfviewer_linux-31.1.19/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"url_launcher_linux","path":"/home/venba/.pub-cache/hosted/pub.dev/url_launcher_linux-3.2.2/","native_build":true,"dependencies":[],"dev_dependency":false}],"windows":[{"name":"connectivity_plus","path":"/home/venba/.pub-cache/hosted/pub.dev/connectivity_plus-6.1.5/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"device_info_plus","path":"/home/venba/.pub-cache/hosted/pub.dev/device_info_plus-11.5.0/","native_build":false,"dependencies":[],"dev_dependency":false},{"name":"file_picker","path":"/home/venba/.pub-cache/hosted/pub.dev/file_picker-10.3.7/","native_build":false,"dependencies":[],"dev_dependency":false},{"name":"firebase_auth","path":"/home/venba/.pub-cache/hosted/pub.dev/firebase_auth-6.1.2/","native_build":true,"dependencies":["firebase_core"],"dev_dependency":false},{"name":"firebase_core","path":"/home/venba/.pub-cache/hosted/pub.dev/firebase_core-4.2.1/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"flutter_secure_storage_windows","path":"/home/venba/.pub-cache/hosted/pub.dev/flutter_secure_storage_windows-3.1.2/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"local_auth_windows","path":"/home/venba/.pub-cache/hosted/pub.dev/local_auth_windows-1.0.11/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"open_file_windows","path":"/home/venba/.pub-cache/hosted/pub.dev/open_file_windows-0.0.3/","native_build":false,"dependencies":[],"dev_dependency":false},{"name":"package_info_plus","path":"/home/venba/.pub-cache/hosted/pub.dev/package_info_plus-8.3.1/","native_build":false,"dependencies":[],"dev_dependency":false},{"name":"path_provider_windows","path":"/home/venba/.pub-cache/hosted/pub.dev/path_provider_windows-2.3.0/","native_build":false,"dependencies":[],"dev_dependency":false},{"name":"shared_preferences_windows","path":"/home/venba/.pub-cache/hosted/pub.dev/shared_preferences_windows-2.4.1/","native_build":false,"dependencies":["path_provider_windows"],"dev_dependency":false},{"name":"syncfusion_pdfviewer_windows","path":"/home/venba/.pub-cache/hosted/pub.dev/syncfusion_pdfviewer_windows-31.1.19/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"url_launcher_windows","path":"/home/venba/.pub-cache/hosted/pub.dev/url_launcher_windows-3.1.5/","native_build":true,"dependencies":[],"dev_dependency":false}],"web":[{"name":"connectivity_plus","path":"/home/venba/.pub-cache/hosted/pub.dev/connectivity_plus-6.1.5/","dependencies":[],"dev_dependency":false},{"name":"device_info_plus","path":"/home/venba/.pub-cache/hosted/pub.dev/device_info_plus-11.5.0/","dependencies":[],"dev_dependency":false},{"name":"file_picker","path":"/home/venba/.pub-cache/hosted/pub.dev/file_picker-10.3.7/","dependencies":[],"dev_dependency":false},{"name":"firebase_auth_web","path":"/home/venba/.pub-cache/hosted/pub.dev/firebase_auth_web-6.1.0/","dependencies":["firebase_core_web"],"dev_dependency":false},{"name":"firebase_core_web","path":"/home/venba/.pub-cache/hosted/pub.dev/firebase_core_web-3.3.0/","dependencies":[],"dev_dependency":false},{"name":"firebase_messaging_web","path":"/home/venba/.pub-cache/hosted/pub.dev/firebase_messaging_web-4.1.0/","dependencies":["firebase_core_web"],"dev_dependency":false},{"name":"flutter_secure_storage_web","path":"/home/venba/.pub-cache/hosted/pub.dev/flutter_secure_storage_web-1.2.1/","dependencies":[],"dev_dependency":false},{"name":"fluttertoast","path":"/home/venba/.pub-cache/hosted/pub.dev/fluttertoast-9.0.0/","dependencies":[],"dev_dependency":false},{"name":"google_sign_in_web","path":"/home/venba/.pub-cache/hosted/pub.dev/google_sign_in_web-1.1.0/","dependencies":[],"dev_dependency":false},{"name":"open_file_web","path":"/home/venba/.pub-cache/hosted/pub.dev/open_file_web-0.0.4/","dependencies":[],"dev_dependency":false},{"name":"package_info_plus","path":"/home/venba/.pub-cache/hosted/pub.dev/package_info_plus-8.3.1/","dependencies":[],"dev_dependency":false},{"name":"pdf_render","path":"/home/venba/.pub-cache/hosted/pub.dev/pdf_render-1.4.12/","dependencies":[],"dev_dependency":false},{"name":"shared_preferences_web","path":"/home/venba/.pub-cache/hosted/pub.dev/shared_preferences_web-2.4.3/","dependencies":[],"dev_dependency":false},{"name":"syncfusion_pdfviewer_web","path":"/home/venba/.pub-cache/hosted/pub.dev/syncfusion_pdfviewer_web-31.1.19/","dependencies":[],"dev_dependency":false},{"name":"url_launcher_web","path":"/home/venba/.pub-cache/hosted/pub.dev/url_launcher_web-2.4.1/","dependencies":[],"dev_dependency":false}]},"dependencyGraph":[{"name":"connectivity_plus","dependencies":[]},{"name":"device_info_plus","dependencies":[]},{"name":"file_picker","dependencies":["flutter_plugin_android_lifecycle"]},{"name":"firebase_auth","dependencies":["firebase_auth_web","firebase_core"]},{"name":"firebase_auth_web","dependencies":["firebase_core","firebase_core_web"]},{"name":"firebase_core","dependencies":["firebase_core_web"]},{"name":"firebase_core_web","dependencies":[]},{"name":"firebase_messaging","dependencies":["firebase_core","firebase_messaging_web"]},{"name":"firebase_messaging_web","dependencies":["firebase_core","firebase_core_web"]},{"name":"flutter_plugin_android_lifecycle","dependencies":[]},{"name":"flutter_secure_storage","dependencies":["flutter_secure_storage_linux","flutter_secure_storage_macos","flutter_secure_storage_web","flutter_secure_storage_windows"]},{"name":"flutter_secure_storage_linux","dependencies":[]},{"name":"flutter_secure_storage_macos","dependencies":[]},{"name":"flutter_secure_storage_web","dependencies":[]},{"name":"flutter_secure_storage_windows","dependencies":["path_provider"]},{"name":"fluttertoast","dependencies":[]},{"name":"google_sign_in","dependencies":["google_sign_in_android","google_sign_in_ios","google_sign_in_web"]},{"name":"google_sign_in_android","dependencies":[]},{"name":"google_sign_in_ios","dependencies":[]},{"name":"google_sign_in_web","dependencies":[]},{"name":"local_auth","dependencies":["local_auth_android","local_auth_darwin","local_auth_windows"]},{"name":"local_auth_android","dependencies":["flutter_plugin_android_lifecycle"]},{"name":"local_auth_darwin","dependencies":[]},{"name":"local_auth_windows","dependencies":[]},{"name":"open_file","dependencies":["open_file_android","open_file_web","open_file_ios","open_file_mac","open_file_windows","open_file_linux"]},{"name":"open_file_android","dependencies":[]},{"name":"open_file_ios","dependencies":[]},{"name":"open_file_linux","dependencies":[]},{"name":"open_file_mac","dependencies":[]},{"name":"open_file_web","dependencies":[]},{"name":"open_file_windows","dependencies":[]},{"name":"package_info_plus","dependencies":[]},{"name":"path_provider","dependencies":["path_provider_android","path_provider_foundation","path_provider_linux","path_provider_windows"]},{"name":"path_provider_android","dependencies":[]},{"name":"path_provider_foundation","dependencies":[]},{"name":"path_provider_linux","dependencies":[]},{"name":"path_provider_windows","dependencies":[]},{"name":"pdf_render","dependencies":[]},{"name":"shared_preferences","dependencies":["shared_preferences_android","shared_preferences_foundation","shared_preferences_linux","shared_preferences_web","shared_preferences_windows"]},{"name":"shared_preferences_android","dependencies":[]},{"name":"shared_preferences_foundation","dependencies":[]},{"name":"shared_preferences_linux","dependencies":["path_provider_linux"]},{"name":"shared_preferences_web","dependencies":[]},{"name":"shared_preferences_windows","dependencies":["path_provider_windows"]},{"name":"syncfusion_flutter_pdfviewer","dependencies":["url_launcher","device_info_plus","syncfusion_pdfviewer_web","syncfusion_pdfviewer_macos","syncfusion_pdfviewer_windows","syncfusion_pdfviewer_linux"]},{"name":"syncfusion_pdfviewer_linux","dependencies":[]},{"name":"syncfusion_pdfviewer_macos","dependencies":[]},{"name":"syncfusion_pdfviewer_web","dependencies":[]},{"name":"syncfusion_pdfviewer_windows","dependencies":[]},{"name":"url_launcher","dependencies":["url_launcher_android","url_launcher_ios","url_launcher_linux","url_launcher_macos","url_launcher_web","url_launcher_windows"]},{"name":"url_launcher_android","dependencies":[]},{"name":"url_launcher_ios","dependencies":[]},{"name":"url_launcher_linux","dependencies":[]},{"name":"url_launcher_macos","dependencies":[]},{"name":"url_launcher_web","dependencies":[]},{"name":"url_launcher_windows","dependencies":[]}],"date_created":"2025-12-16 18:42:23.775793","version":"3.32.1","swift_package_manager_enabled":{"ios":false,"macos":false}} \ No newline at end of file diff --git a/lib/core/routing/app_router.dart b/lib/core/routing/app_router.dart index 42e99c2..7ac7f4c 100644 --- a/lib/core/routing/app_router.dart +++ b/lib/core/routing/app_router.dart @@ -18,6 +18,7 @@ import '../../presentation/screens/StaffAttendance/attendanceAllDetails.dart'; import '../../presentation/screens/StaffAttendance/individual_Attendance.dart'; import '../../presentation/screens/UserManagement/Agent/agent.dart'; import '../../presentation/screens/UserManagement/Agent/agentList.dart'; +import '../../presentation/screens/UserManagement/POS/pos_list.dart'; import '../../presentation/screens/UserManagement/Profile/profile_mobile.dart'; import '../../presentation/screens/UserManagement/SalesExecutive/salesExecutive.dart'; import '../../presentation/screens/UserManagement/SalesExecutive/salesExecutiveList.dart'; @@ -85,57 +86,61 @@ final GoRouter appRouter = GoRouter( builder: (context, state) => const AgentList(), ), - GoRoute( - path: '/agent/:id', - builder: (context, state) { - final id = state.pathParameters['id']!; - - // if "create", open empty form - if (id == 'create') { - return const Agent(); // create mode - } else { - return Agent(id: id); // update mode - } - }, - ), - + // GoRoute( + // path: '/agent/:id', + // builder: (context, state) { + // final id = state.pathParameters['id']!; + // + // // if "create", open empty form + // if (id == 'create') { + // return const Agent(); // create mode + // } else { + // return Agent(id: id); // update mode + // } + // }, + // ), GoRoute( path: AppRoutes.staffLst, // builder: (context, state) => const Agent(), builder: (context, state) => const StaffList(), ), + // GoRoute( + // path: '/staff/:id', + // builder: (context, state) { + // final id = state.pathParameters['id']!; + // // if "create", open empty form + // if (id == 'create') { + // return const Staff(); // create mode + // } else { + // return Staff(id: id); // update mode + // } + // }, + // ), GoRoute( - path: '/staff/:id', - builder: (context, state) { - final id = state.pathParameters['id']!; - // if "create", open empty form - if (id == 'create') { - return const Staff(); // create mode - } else { - return Staff(id: id); // update mode - } - }, + path: AppRoutes.posLst, + // builder: (context, state) => const Agent(), + builder: (context, state) => const PosList(), ), + GoRoute( path: AppRoutes.salesExecutiveLst, // builder: (context, state) => const Agent(), builder: (context, state) => const SalesExecutiveList(), ), - GoRoute( - path: '/salesExecutive/:id', - builder: (context, state) { - final id = state.pathParameters['id']!; - // if "create", open empty form - if (id == 'create') { - return const SalesExecutive(); // create mode - } else { - return SalesExecutive(id: id); // update mode - } - }, - ), - + // GoRoute( + // path: '/salesExecutive/:id', + // builder: (context, state) { + // final id = state.pathParameters['id']!; + // // if "create", open empty form + // if (id == 'create') { + // return const SalesExecutive(); // create mode + // } else { + // return SalesExecutive(id: id); // update mode + // } + // }, + // ), GoRoute( path: AppRoutes.enquiryLst, builder: (context, state) => const EnquiryList(), @@ -194,8 +199,10 @@ final GoRouter appRouter = GoRouter( return CustomTransitionPage( key: state.pageKey, - opaque: true, // ⭐ required for open animation - barrierColor: Colors.black.withOpacity(0.3), // optional dim background + opaque: true, // ⭐ required for open animation + barrierColor: Colors.black.withOpacity( + 0.3, + ), // optional dim background fullscreenDialog: true, transitionDuration: const Duration(milliseconds: 1000), @@ -210,7 +217,7 @@ final GoRouter appRouter = GoRouter( return SlideTransition( position: Tween( - begin: const Offset(0, 1), // slide from bottom + begin: const Offset(0, 1), // slide from bottom end: Offset.zero, ).animate(curved), child: child, diff --git a/lib/core/routing/routes.dart b/lib/core/routing/routes.dart index 21139e9..ecb84b8 100644 --- a/lib/core/routing/routes.dart +++ b/lib/core/routing/routes.dart @@ -10,6 +10,7 @@ class AppRoutes { static const staff = '/staff/:id'; static const String salesExecutiveLst = '/salesExecutiveLst'; static const salesExecutive = '/salesExecutive/:id'; + static const String posLst = '/posLst'; static const String enquiryLst = '/enquiryLst'; static const String enquiryForStaff = '/enquiryForStaff'; static const String tabEnquiry = '/tabEnquiry'; diff --git a/lib/core/services/api_service.dart b/lib/core/services/api_service.dart index f18685e..3ac2bb4 100644 --- a/lib/core/services/api_service.dart +++ b/lib/core/services/api_service.dart @@ -307,6 +307,8 @@ class ApiService { // return; // } final url = Uri.parse('https://venbait.in/nhance/partner/dev/$path'); + // final url = Uri.parse('${Env.apiUrl}$path'); + // final url = Uri.parse('${Env.apiUrl}$path'); print('getPdfDownload 2 - $url'); await _initializeToken(); @@ -387,6 +389,183 @@ class ApiService { } } + Future generateChartExcel( + // BuildContext context, + String path, + String id, + String month, + dynamic managerId, + ) async { + print('getPdfDownload 1 - $path'); + + dynamic pathVal; + if (path == 'Insurer') { + pathVal = + 'dashboard/downloadInsurerPoliciesExcel?manager_id=$managerId&month=$month&insurer_id=$id'; + } else if (path == 'Broker') { + pathVal = + 'dashboard/downloadBrokerPoliciesExcel?manager_id=$managerId&month=$month&broker_id=$id'; + } else if (path == 'Product') { + pathVal = + 'dashboard/downloadProductPoliciesExcel?manager_id=$managerId&month=$month&vehicle_type=$id'; + } else if (path == 'PerformingTop50') { + pathVal = + 'dashboard/downloadAgentMonthlyPoliciesExcel?manager_id=$managerId&agent_id=$id'; + } else if (path == 'NonPerformingBelow50K') { + pathVal = 'dashboard/downloadLowPremiumAgentExcel?manager_id=$managerId'; + } else { + //NoBusiness + pathVal = + 'dashboard/downloadAgentsWithoutPoliciesExcel?manager_id=$managerId'; + } + + final url = Uri.parse('${Env.apiUrl}$pathVal'); + // final url = Uri.parse('https://venbait.in/nhance/partner/dev/$path'); + print('getPdfDownload 2 - $url'); + await _initializeToken(); + + if (_token == null) throw Exception('Token not found. Please log in.'); + + final headers = { + 'Authorization': 'Bearer $_token', + // 'App-Signature': Env.App_Signature, + 'app-signature': Env.App_Signature, + }; + + final response = await _makeGethttpRequest(url, headers); + + if (response.statusCode == 200) { + final contentType = response.headers['content-type'] ?? ''; + if (contentType.contains('application/json')) { + final jsonResponse = jsonDecode(response.body); + if (jsonResponse['code'] == 404 || jsonResponse['code'] == 500) { + ToastHelper.show('File Not Found', Colors.red); + } else { + ToastHelper.show('No File Found', Colors.red); + } + return; + } else { + final blob = html.Blob([response.bodyBytes]); + final blobUrl = html.Url.createObjectUrlFromBlob(blob); + print('getPdfDownload $blobUrl'); + final contentDisp = response.headers['content-disposition']; + print('getPdfDownloadcontentDisp $contentDisp'); + + String fileName = extractFileName(contentDisp, path.split('/').last); + print('Resolved fileName: $fileName'); + final anchor = html.AnchorElement(href: blobUrl) + ..setAttribute('download', fileName) + ..click(); + + html.Url.revokeObjectUrl(blobUrl); + } + } else if (response.statusCode == 404) { + showDialog( + context: context, + builder: (context) => AlertDialog( + title: const Text('File not found.'), + actions: [ + TextButton( + child: const Text('OK'), + onPressed: () => Navigator.of(context).pop(), + ), + ], + ), + ); + } else if (response.statusCode == 403) { + await clearLocalStorageAndRedirect(); + } else if (response.statusCode == 500) { + ToastHelper.show('No File Found', Colors.red); + } else if (response.statusCode == 302) { + print('getPdfDownload 3-PP-302'); + ToastHelper.show('No File Found', Colors.red); + } else { + ToastHelper.show('No File Found', Colors.red); + throw Exception('Failed to download file'); + } + } + + Future generatePerformanceDashboardExcel( + // BuildContext context, + String path, + String id, + String salesId, + String month, + dynamic managerId, + ) async { + print('getPdfDownload 1 - $path'); + + dynamic pathVal; + + pathVal = + 'dashboard/downloadStaffAndProductPoliciesExcel?manager_id=$managerId&month=$month&sales_executive_id=$salesId&vehicle_type=$id'; + + final url = Uri.parse('${Env.apiUrl}$pathVal'); + // final url = Uri.parse('https://venbait.in/nhance/partner/dev/$path'); + print('getPdfDownload 2 - $url'); + await _initializeToken(); + + if (_token == null) throw Exception('Token not found. Please log in.'); + + final headers = { + 'Authorization': 'Bearer $_token', + // 'App-Signature': Env.App_Signature, + 'app-signature': Env.App_Signature, + }; + + final response = await _makeGethttpRequest(url, headers); + + if (response.statusCode == 200) { + final contentType = response.headers['content-type'] ?? ''; + if (contentType.contains('application/json')) { + final jsonResponse = jsonDecode(response.body); + if (jsonResponse['code'] == 404 || jsonResponse['code'] == 500) { + ToastHelper.show('File Not Found', Colors.red); + } else { + ToastHelper.show('No File Found', Colors.red); + } + return; + } else { + final blob = html.Blob([response.bodyBytes]); + final blobUrl = html.Url.createObjectUrlFromBlob(blob); + print('getPdfDownload $blobUrl'); + final contentDisp = response.headers['content-disposition']; + print('getPdfDownloadcontentDisp $contentDisp'); + + String fileName = extractFileName(contentDisp, path.split('/').last); + print('Resolved fileName: $fileName'); + final anchor = html.AnchorElement(href: blobUrl) + ..setAttribute('download', fileName) + ..click(); + + html.Url.revokeObjectUrl(blobUrl); + } + } else if (response.statusCode == 404) { + showDialog( + context: context, + builder: (context) => AlertDialog( + title: const Text('File not found.'), + actions: [ + TextButton( + child: const Text('OK'), + onPressed: () => Navigator.of(context).pop(), + ), + ], + ), + ); + } else if (response.statusCode == 403) { + await clearLocalStorageAndRedirect(); + } else if (response.statusCode == 500) { + ToastHelper.show('No File Found', Colors.red); + } else if (response.statusCode == 302) { + print('getPdfDownload 3-PP-302'); + ToastHelper.show('No File Found', Colors.red); + } else { + ToastHelper.show('No File Found', Colors.red); + throw Exception('Failed to download file'); + } + } + Future downloadFile({ required String? apiUrl, required PlatformFile? localFile, @@ -754,7 +933,9 @@ class ApiService { } else if (role == 'Accounts') { print('manager'); // url = Uri.parse('${Env.apiUrl}staff/staffList'); - url = Uri.parse('${Env.apiUrl}staff/staffList?manager_id=${managerId}'); + url = Uri.parse( + '${Env.apiUrl}salesExecutive/executiveList?manager_id=${managerId}', + ); } else { print('handler'); url = Uri.parse( @@ -786,6 +967,36 @@ class ApiService { return response; } + // ----------------------------------- POS ------------------------------------------------- + Future> fetchPosList(int managerId, val) async { + // print(_token); + + print('dropDown - $managerId'); + if (_token == null) { + await _initializeToken(); + } + + dynamic url; + if (val == 'dropDown') { + print('dropDown'); + url = Uri.parse( + '${Env.apiUrl}master/getAllPOS?is_active=1&manager_id=${managerId}', + ); + } else { + print('List'); + url = Uri.parse( + '${Env.apiUrl}master/getAllPOS?is_active=1&manager_id=${managerId}', + ); + } + + final headers = { + 'Authorization': 'Bearer $_token' ?? '', + 'app-signature': Env.App_Signature, + }; + final response = await _makeGetRequest(url, headers); + return response; + } + // ----------------------------------- ENQUIRY ------------------------------------------------- Future> fetchEnquiryList( @@ -1504,14 +1715,19 @@ class ApiService { // --------------------------------- PayOut Module---------------------------------------------- Future> getCommissionRateList(data) async { + print("getCommissionRateList------- $data}"); final url = Uri.parse('${Env.apiUrl}/invoice/commission-rate-list'); - + print("getCommissionRateList 1"); // final token = await getToken(); // Fetch token - if (_token == null) { - throw Exception('Token not found. Please log in.'); - } + // if (_token == null) { + // throw Exception('Token not found. Please log in.'); + // } + if (_token == null) { + await _initializeToken(); + } + print("getCommissionRateList 2"); print("data------- $data}"); final headers = { @@ -1526,7 +1742,7 @@ class ApiService { } Future> getCreateOrUpdate(data) async { - final url = Uri.parse('${Env.apiUrl}/invoice/create-or-update'); + final url = Uri.parse('${Env.apiUrl}invoice/create-or-update'); // final token = await getToken(); // Fetch token @@ -1666,4 +1882,50 @@ class ApiService { final response = await _makePostRequestJson(url, data, headers); return response; } + + Future> calculateCommissionRequest(data) async { + final url = Uri.parse('${Env.apiUrl}policy/calculateCommissionRequest'); + + // final token = await getToken(); // Fetch token + + if (_token == null) { + throw Exception('Token not found. Please log in.'); + } + + print("data------- $data}"); + + final headers = { + 'Authorization': 'Bearer $_token', + 'Content-Type': 'application/json', + 'app-signature': Env.App_Signature, + // 'app-signature': 'nhance-partner-2025-signature-35468846JRhH551HK', + }; + + final response = await _makePostRequestJson(url, data, headers); + return response; + } + + Future> fetchPolicyMasterDropDown(String val) async { + // print(_token); + if (_token == null) { + await _initializeToken(); + } + + print('VAl - $val'); + + dynamic url; + + if (val == 'fuelType') { + url = Uri.parse('${Env.apiUrl}policy/fuelTypeMaster'); + } else if (val == 'vehicleType') { + url = Uri.parse('${Env.apiUrl}policy/vehicleTypeMaster'); + } + + final headers = { + 'Authorization': 'Bearer $_token' ?? '', + 'app-signature': Env.App_Signature, + }; + final response = await _makeGetRequest(url, headers); + return response; + } } diff --git a/lib/presentation/layouts/appheader.dart b/lib/presentation/layouts/appheader.dart index d2fd65e..c3d7787 100644 --- a/lib/presentation/layouts/appheader.dart +++ b/lib/presentation/layouts/appheader.dart @@ -67,8 +67,8 @@ class _AppHeaderState extends ConsumerState { } else if (currentRoute.contains('broker') || currentRoute.contains('payment')) { _activeMenu = 'Masters'; - } else if (currentRoute.contains('invoice')) { - _activeMenu = 'Pay Out'; + } else if (currentRoute.contains('payout')) { + _activeMenu = 'Invoice'; } }); }); @@ -155,6 +155,15 @@ class _AppHeaderState extends ConsumerState { context.go(AppRoutes.salesExecutiveLst); }, ), + const SizedBox(height: 2), + _buildPopupItem( + label: "POS", + onTap: () { + _hidePopup(); + setState(() => _activeMenu = 'User'); + context.go(AppRoutes.posLst); + }, + ), ], if (key == 'Reports') ...[ if (role == 'manager') ...[ @@ -330,12 +339,12 @@ class _AppHeaderState extends ConsumerState { // Pay Out button for Accounts if (role == 'Accounts') ...[ _buildMenuItem( - isActive: _activeMenu == 'Pay Out', + isActive: _activeMenu == 'Invoice', icon: Icons.checklist_outlined, - label: "Pay Out", + label: "Invoice", onTap: () async { _hidePopup(); - setState(() => _activeMenu = 'Pay Out'); + setState(() => _activeMenu = 'Invoice'); await _clearDashboardFilters(); context.go(AppRoutes.invoiceList); }, diff --git a/lib/presentation/screens/Masters/Brokers/broker.dart b/lib/presentation/screens/Masters/Brokers/broker.dart index 407c78a..cd43a14 100644 --- a/lib/presentation/screens/Masters/Brokers/broker.dart +++ b/lib/presentation/screens/Masters/Brokers/broker.dart @@ -12,6 +12,7 @@ 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 'package:nhance_partner/presentation/themes/indicators/text_field_theme_inline_editor.dart'; import '../../../../core/config/env.dart'; import '../../../../core/services/api_service.dart'; @@ -69,7 +70,11 @@ class BrokerState extends ConsumerState { dynamic managerId; Map dataDetails() { - final data = {"name": controllers["name"]?.text, "is_active": isActive}; + final data = { + "name": controllers["name"]?.text, + "short_name": controllers["code"]?.text, + "is_active": isActive, + }; return data; } @@ -108,12 +113,16 @@ class BrokerState extends ConsumerState { setState(() { selectedId = id; controllers['name']?.text = data['name'] ?? ''; + controllers['code']?.text = data['short_name'] ?? ''; controllers["is_active"]?.text = data['is_active'].toString(); }); } } Future handleSave() async { + // if (controllers['name'] != null && controllers['name'] != '') { + // return; + // } if (!_formKey.currentState!.validate()) return; setState(() { if (_formKey.currentState!.validate()) { @@ -185,17 +194,12 @@ class BrokerState extends ConsumerState { print("Broker submitted successfully!"); print("Response: ${response.body}"); - if(responseBody['status'] == 200){ + if (responseBody['status'] == 200) { refresh(); isUpdating - ? ToastHelper.showSuccessToast( - context,message - ) - : ToastHelper.showSuccessToast( - context,message, - ); + ? ToastHelper.showSuccessToast(context, message) + : ToastHelper.showSuccessToast(context, message); } else { - print("Failed to submit plan. Status: ${response.statusCode}"); print("Error: ${response.body}"); @@ -281,7 +285,9 @@ class BrokerState extends ConsumerState { Widget buildFormFields() { return Form( key: _formKey, - child: Column(children: [buildName()]), + child: Row( + children: [buildName(), SizedBox(width: 10), buildShortName()], + ), ); } @@ -291,13 +297,39 @@ class BrokerState extends ConsumerState { children: [ Text("Broker *", style: _textStyle), SizedBox(width: 10), - ThemedFormField( - controller: controllers['name']!, - validator: (value) => Validators.requiredField(value, "name"), - txtwidth: MediaQuery.of(context).size.width * 0.15, - borderColor: Color(0xFFE2E8F0), - highlightColor: Color(0xFF50A398), - txtheight: 30, + SizedBox( + child: ThemedFormInlineField( + controller: controllers['name']!, + validator: (value) => Validators.requiredField(value, "name"), + txtwidth: MediaQuery.of(context).size.width * 0.15, + borderColor: Color(0xFFE2E8F0), + highlightColor: Color(0xFF50A398), + // txtheight: 30, + isdense: true, + errFieldHgt: 0, + ), + ), + ], + ); + } + + Widget buildShortName() { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text("Broker Code *", style: _textStyle), + SizedBox(width: 10), + SizedBox( + child: ThemedFormInlineField( + controller: controllers['code']!, + validator: (value) => Validators.requiredField(value, "code"), + txtwidth: MediaQuery.of(context).size.width * 0.15, + borderColor: Color(0xFFE2E8F0), + highlightColor: Color(0xFF50A398), + // txtheight: 30, + isdense: true, + errFieldHgt: 0, + ), ), ], ); diff --git a/lib/presentation/screens/Masters/Brokers/brokerList.dart b/lib/presentation/screens/Masters/Brokers/brokerList.dart index 06db986..03b45cb 100644 --- a/lib/presentation/screens/Masters/Brokers/brokerList.dart +++ b/lib/presentation/screens/Masters/Brokers/brokerList.dart @@ -283,9 +283,13 @@ class BrokerListState extends ConsumerState { child: Text('S.No.', style: _headerStyle), ), Expanded( - flex: 2, + flex: 1, child: Text('Broker', style: _headerStyle), ), + Expanded( + flex: 1, + child: Text('Broker Code', style: _headerStyle), + ), Expanded( flex: 1, @@ -383,7 +387,11 @@ class BrokerListState extends ConsumerState { child: Row( children: [ Expanded(flex: 1, child: Text('$sno' ?? '-', style: _dataBold)), - Expanded(flex: 2, child: Text(item['name'] ?? '-', style: _dataBold)), + Expanded(flex: 1, child: Text(item['name'] ?? '-', style: _dataBold)), + Expanded( + flex: 1, + child: Text(item['short_name'] ?? '-', style: _dataBold), + ), Expanded( flex: 1, child: Row( @@ -466,17 +474,16 @@ class BrokerListState extends ConsumerState { ); } - static final _dataBold = GoogleFonts.inter( - fontSize: 11.5, - fontWeight: FontWeight.w400, - color: Color(0xFF000000), - ); - static final _dataSub = GoogleFonts.inter( fontSize: 10, fontWeight: FontWeight.w300, color: Color(0xFF585757), ); + static final _dataBold = GoogleFonts.inter( + fontSize: 11.5, + fontWeight: FontWeight.w400, + color: Color(0xFF000000), + ); static final _headerStyle = GoogleFonts.poppins( fontSize: 11.2, diff --git a/lib/presentation/screens/Masters/PaymentMode/payment.dart b/lib/presentation/screens/Masters/PaymentMode/payment.dart index e956b25..af50815 100644 --- a/lib/presentation/screens/Masters/PaymentMode/payment.dart +++ b/lib/presentation/screens/Masters/PaymentMode/payment.dart @@ -114,6 +114,12 @@ class PaymentState extends ConsumerState { } Future handleSave() async { + if (controllers['value'] == null || + controllers['value']!.text.trim().isEmpty) { + ToastHelper.showWarningToast(context, "Payment Mode is required"); + return; + } + if (!_formKey.currentState!.validate()) return; setState(() { if (_formKey.currentState!.validate()) { @@ -194,38 +200,33 @@ class PaymentState extends ConsumerState { print("Response: ${response.body}"); print(message); - if(responseBody['status'] == 200){ - refresh(); - isUpdating - ? ToastHelper.showSuccessToast( - context,message, - ) - : ToastHelper.showSuccessToast( - context,message, - ); - } else { - print("Failed to submit plan. Status: ${response.statusCode}"); - print("Error: ${response.body}"); - - showDialog( - context: context, - builder: (BuildContext context) { - return AlertDialog( - title: Text("Payment Creation Failed"), - content: Text(message), - actions: [ - TextButton( - child: Text("OK"), - onPressed: () { - Navigator.of(context).pop(); - }, - ), - ], - ); - }, - ); - } + if (responseBody['status'] == 200) { + refresh(); + isUpdating + ? ToastHelper.showSuccessToast(context, message) + : ToastHelper.showSuccessToast(context, message); + } else { + print("Failed to submit plan. Status: ${response.statusCode}"); + print("Error: ${response.body}"); + showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + title: Text("Payment Creation Failed"), + content: Text(message), + actions: [ + TextButton( + child: Text("OK"), + onPressed: () { + Navigator.of(context).pop(); + }, + ), + ], + ); + }, + ); + } } else if (response.statusCode == 403) { await apiService.clearLocalStorageAndRedirect(); } @@ -302,11 +303,13 @@ class PaymentState extends ConsumerState { SizedBox(width: 10), ThemedFormField( controller: controllers['value']!, - validator: (value) => Validators.requiredField(value, "value"), + // validator: (value) => Validators.requiredField(value, "value"), txtwidth: MediaQuery.of(context).size.width * 0.15, borderColor: Color(0xFFE2E8F0), highlightColor: Color(0xFF50A398), txtheight: 30, + isdense: true, + errFieldFont: 0, ), ], ); diff --git a/lib/presentation/screens/UserManagement/Agent/agent.dart b/lib/presentation/screens/UserManagement/Agent/agent.dart index 3b175fb..c428481 100644 --- a/lib/presentation/screens/UserManagement/Agent/agent.dart +++ b/lib/presentation/screens/UserManagement/Agent/agent.dart @@ -35,7 +35,9 @@ import 'package:universal_html/html.dart' as html; class Agent extends ConsumerStatefulWidget { final String? id; - const Agent({super.key, this.id}); + final void Function(String value) onSubmit; + const Agent({super.key, this.id, required this.onSubmit}); + @override ConsumerState createState() => AgentState(); } @@ -43,7 +45,14 @@ class Agent extends ConsumerStatefulWidget { class AgentState extends ConsumerState { final _formKey = GlobalKey(); late ApiService apiService; - List tabHeader = ['name', 'email', 'mobile', 'code', 'address']; + List tabHeader = [ + 'name', + 'email', + 'mobile', + 'code', + 'address', + 'retenRate', + ]; late String isActive = "1"; // html.File? docUploadedFile; // PlatformFile? passportFile; @@ -73,6 +82,7 @@ class AgentState extends ConsumerState { "mobile": controllers["mobile"]?.text, "address": controllers["address"]?.text, "agent_code": controllers["code"]?.text, + "retention_rate": controllers["retenRate"]?.text, 'sales_executive_id': selectedSalesExectv, "is_active": isActive, "manager_id": managerId, @@ -85,6 +95,8 @@ class AgentState extends ConsumerState { super.initState(); apiService = ApiService(); + print('AGID - ${widget.id}'); + for (String field in tabHeader) { controllers[field] = TextEditingController(); } @@ -156,8 +168,10 @@ class AgentState extends ConsumerState { controllers['mobile']?.text = data['mobile'] ?? ''; controllers['code']?.text = data['agent_code'] ?? ''; controllers['address']?.text = data['address'] ?? ''; + controllers['retenRate']?.text = data['retention_rate'] ?? ''; isActive = data["is_active"]; selectedSalesExectv = data['sales_executive_id'] ?? ''; + print('selectedSalesExectv - $selectedSalesExectv'); String? apiDocPath = data["certificate_file_name"]; if (apiDocPath != null && apiDocPath.isNotEmpty) { @@ -182,7 +196,7 @@ class AgentState extends ConsumerState { final email = controllers['email']!.text.trim(); final phone = controllers['mobile']!.text.trim(); - // 🔴 Conditional validation: at least one required + // Conditional validation: at least one required if (email.isEmpty && phone.isEmpty) { ToastHelper.showSuccessToast( context, @@ -317,7 +331,11 @@ class AgentState extends ConsumerState { context, 'Partner Created Successfully', ); - context.go(AppRoutes.agentLst); + + Navigator.of(context).pop(); + + widget.onSubmit("success"); + // context.go(AppRoutes.agentLst); } else if (response.statusCode == 403) { await apiService.clearLocalStorageAndRedirect(); } else { @@ -395,11 +413,12 @@ class AgentState extends ConsumerState { Widget build(BuildContext context) { // managerId = ref.watch(managerIdProvider); // userId = ref.watch(userProvider); - return MainLayout( - title: "Partner", - body: Container( + return AlertDialog( + backgroundColor: Colors.white, + content: Container( // color: Colors.yellow.shade50, - width: MediaQuery.of(context).size.width, + width: MediaQuery.of(context).size.width * 0.62, + height: MediaQuery.of(context).size.height * 0.7, child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start, @@ -407,7 +426,7 @@ class AgentState extends ConsumerState { Container( // height: 30, // color: Colors.red.shade50, - width: MediaQuery.of(context).size.width, + width: MediaQuery.of(context).size.width * 0.8, child: GestureDetector( onTap: () { context.go(AppRoutes.agentLst); @@ -416,24 +435,24 @@ class AgentState extends ConsumerState { crossAxisAlignment: CrossAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.start, children: [ - Tooltip( - message: 'Back', - child: IconButton( - icon: const Icon( - Icons.arrow_left_sharp, - size: 25, - color: Color(0xFF425B5B), - ), - onPressed: () { - context.go(AppRoutes.agentLst); - }, - splashRadius: 18, - hoverColor: Colors.black12, - padding: const EdgeInsets.all(4), - constraints: const BoxConstraints(), - ), - ), - const SizedBox(width: 5), // spacing between icon and text + // Tooltip( + // message: 'Back', + // child: IconButton( + // icon: const Icon( + // Icons.arrow_left_sharp, + // size: 25, + // color: Color(0xFF425B5B), + // ), + // onPressed: () { + // context.go(AppRoutes.agentLst); + // }, + // splashRadius: 18, + // hoverColor: Colors.black12, + // padding: const EdgeInsets.all(4), + // constraints: const BoxConstraints(), + // ), + // ), + const SizedBox(width: 15), // spacing between icon and text Text( "Partner", style: GoogleFonts.poppins( @@ -441,16 +460,32 @@ class AgentState extends ConsumerState { fontWeight: FontWeight.w500, ), ), + Spacer(), + 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: Tooltip( + message: 'Close', + child: const Icon(Icons.close, size: 18), + ), + ), + ), + // const SizedBox(width: 15), ], ), ), ), - SizedBox(height: 10), + // SizedBox(height: 10), Expanded( child: Container( // color: Colors.green, - width: MediaQuery.of(context).size.width, - margin: EdgeInsets.all(8.0), + // width: MediaQuery.of(context).size.width, + // margin: EdgeInsets.all(8.0), padding: EdgeInsets.symmetric(horizontal: 14.0, vertical: 20.0), decoration: BoxDecoration( borderRadius: BorderRadius.circular(8.0), @@ -459,7 +494,7 @@ class AgentState extends ConsumerState { ), child: Column( children: [ - Row(children: [Expanded(child: buildFormFields())]), + Row(children: [buildFormFields()]), Spacer(), Row( mainAxisAlignment: MainAxisAlignment.end, @@ -507,39 +542,42 @@ class AgentState extends ConsumerState { return Form( key: _formKey, child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ - Expanded(child: buildName()), + buildName(), SizedBox(width: 25), - Expanded(child: buildEmail()), + buildEmail(), + SizedBox(width: 25), + buildPhNumber(), ], ), SizedBox(height: 20), Row( children: [ - Expanded(child: buildPhNumber()), + buildId(), SizedBox(width: 25), - Expanded(child: buildId()), + buildSalesExecutive(context), + SizedBox(width: 25), + buildRetentionRate(context), ], ), SizedBox(height: 20), Row( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, children: [ - Expanded(child: buildSalesExecutive(context)), + buildAddress(), SizedBox(width: 25), - Expanded(child: buildAddress()), + buildUploadDocument(), + SizedBox(width: 25), + + // Expanded(child: SizedBox.shrink()), ], ), - SizedBox(height: 20), - Row( - children: [ - Expanded(child: buildUploadDocument()), - SizedBox(width: 25), - Expanded(child: SizedBox.shrink()), - ], - ), - // Row( + // Column( // children: [ // Expanded(child: buildIncentiveFile()), // SizedBox(width: 25), @@ -552,28 +590,29 @@ class AgentState extends ConsumerState { } Widget buildName() { - return Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + // mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text("Full Name *", style: _textStyle), - SizedBox(width: 10), + SizedBox(height: 10), ThemedFormField( controller: controllers['name']!, validator: (value) => Validators.requiredField(value, "name"), borderColor: Color(0xFFE2E8F0), highlightColor: Color(0xFF50A398), - txtwidth: MediaQuery.of(context).size.width * 0.26, + txtwidth: MediaQuery.of(context).size.width * 0.18, ), ], ); } Widget buildEmail() { - return Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, + return Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ Text("Email *", style: _textStyle), - SizedBox(width: 10), + SizedBox(height: 10), ThemedFormField( controller: controllers['email']!, validator: (value) { @@ -591,18 +630,18 @@ class AgentState extends ConsumerState { inputFormatters: [ FilteringTextInputFormatter.allow(RegExp(r'[a-zA-Z0-9_@.]')), ], - txtwidth: MediaQuery.of(context).size.width * 0.26, + txtwidth: MediaQuery.of(context).size.width * 0.18, ), ], ); } Widget buildPhNumber() { - return Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, + return Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ Text("Phone Number *", style: _textStyle), - SizedBox(width: 10), + SizedBox(height: 10), ThemedFormField( controller: controllers['mobile']!, validator: (value) { @@ -621,18 +660,18 @@ class AgentState extends ConsumerState { inputFormatters: [ FilteringTextInputFormatter.allow(RegExp(r'[0-9]')), ], - txtwidth: MediaQuery.of(context).size.width * 0.26, + txtwidth: MediaQuery.of(context).size.width * 0.18, ), ], ); } Widget buildId() { - return Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, + return Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ Text("Partner Id *", style: _textStyle), - SizedBox(width: 10), + SizedBox(height: 10), ThemedFormField( controller: controllers['code']!, inputFormatters: [ @@ -641,23 +680,55 @@ class AgentState extends ConsumerState { borderColor: Color(0xFFE2E8F0), highlightColor: Color(0xFF50A398), validator: (value) => Validators.requiredField(value, "id"), - txtwidth: MediaQuery.of(context).size.width * 0.26, + txtwidth: MediaQuery.of(context).size.width * 0.18, + ), + ], + ); + } + + Widget buildRetentionRate(context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text("Retention Rate*", style: _textStyle), + SizedBox(height: 10), + ThemedFormField( + controller: controllers['retenRate']!, + inputFormatters: [ + TextInputFormatter.withFunction((oldValue, newValue) { + if (newValue.text.isEmpty) return newValue; + + final value = double.tryParse(newValue.text); + if (value == null) return oldValue; + + // Allow only values <= 10 + if (value <= 10) { + return newValue; + } + return oldValue; + }), + ], + keyboardType: TextInputType.numberWithOptions(decimal: true), + borderColor: Color(0xFFE2E8F0), + highlightColor: Color(0xFF50A398), + validator: (value) => Validators.requiredField(value, "id"), + txtwidth: MediaQuery.of(context).size.width * 0.18, ), ], ); } Widget buildAddress() { - return Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, + return Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ Text("Address", style: _textStyle), - SizedBox(width: 10), + SizedBox(height: 10), ThemedFormField( controller: controllers['address']!, borderColor: Color(0xFFE2E8F0), highlightColor: Color(0xFF50A398), - txtwidth: MediaQuery.of(context).size.width * 0.26, + txtwidth: MediaQuery.of(context).size.width * 0.18, inputFormatters: [ FilteringTextInputFormatter.allow(RegExp(r'[ a-zA-Z0-9_#/.]')), ], @@ -669,17 +740,20 @@ class AgentState extends ConsumerState { Widget buildSalesExecutive(BuildContext context) { Map? selectedroleVal = filteredSalesExecutiveData .firstWhere( - (item) => item['id'].toString() == selectedSalesExectv, + (item) => item['id'] == selectedSalesExectv, orElse: () => {}, ); - final isReadOnly = widget.id != null; + print('selectedroleVal - $selectedroleVal'); - return Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, + final isReadOnly = false; + // final isReadOnly = widget.id != null; + + return Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ Text("Sales Executive *", style: _textStyle), - SizedBox(width: 10), + SizedBox(height: 10), Container( decoration: BoxDecoration( // color: Colors.white, @@ -687,7 +761,7 @@ class AgentState extends ConsumerState { ), width: ResponsiveLayout.isMobile(context) ? null - : MediaQuery.of(context).size.width * 0.26, + : MediaQuery.of(context).size.width * 0.18, // height: 40, child: AbsorbPointer( absorbing: isReadOnly, @@ -815,11 +889,11 @@ class AgentState extends ConsumerState { } Widget buildUploadDocument() { - return Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, + return Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ Text("Upload Certificate", style: _textStyle), - SizedBox(width: 10), + SizedBox(height: 10), Column( mainAxisAlignment: MainAxisAlignment.end, @@ -827,7 +901,8 @@ class AgentState extends ConsumerState { children: [ ThemedUploadField( hintText: selectedFileNames ?? "Upload Document", - txtwidth: MediaQuery.of(context).size.width * 0.26, + txtwidth: MediaQuery.of(context).size.width * 0.18, + txtheight: 45, borderColor: Color(0xFFE2E8F0), // highlightColor: Color(0xFF50A398), onFileSelected: (fileName, file) { @@ -842,7 +917,7 @@ class AgentState extends ConsumerState { if (docUploadedFile != null || passportFileUrlFromApi != null) Container( // color: Colors.white, - child: Row( + child: Column( children: [ // IconButton( // onPressed: () { @@ -873,7 +948,7 @@ class AgentState extends ConsumerState { // color: Color(0xFF425B5B), // color: Colors.green.shade300, ), - child: Row( + child: Column( children: const [ Text( "Download", @@ -901,7 +976,7 @@ class AgentState extends ConsumerState { // children: [ // ThemedUploadField( // hintText: selectedFileNames ?? "Upload Document", - // txtwidth: MediaQuery.of(context).size.width * 0.26, + // txtwidth: MediaQuery.of(context).size.width * 0.18, // // onFileSelected: (fileName, file) { // // print("File picked: ${fileName}"); @@ -920,7 +995,7 @@ class AgentState extends ConsumerState { // // Centers the text // Container( // color: Colors.white, - // child: Row( + // child: Column( // mainAxisAlignment: MainAxisAlignment.end, // crossAxisAlignment: CrossAxisAlignment.end, // children: [ @@ -1009,7 +1084,7 @@ class AgentState extends ConsumerState { // borderRadius: BorderRadius.circular(5), // color: Colors.green.shade300, // ), - // child: Row( + // child: Column( // mainAxisAlignment: MainAxisAlignment.end, // crossAxisAlignment: CrossAxisAlignment.end, // children: [ diff --git a/lib/presentation/screens/UserManagement/Agent/agentList.dart b/lib/presentation/screens/UserManagement/Agent/agentList.dart index 757d1d9..19a960e 100644 --- a/lib/presentation/screens/UserManagement/Agent/agentList.dart +++ b/lib/presentation/screens/UserManagement/Agent/agentList.dart @@ -15,6 +15,7 @@ import '../../../themes/indicators/filter_btn.dart'; import '../../../themes/indicators/search_field_theme.dart'; import '../../../themes/indicators/text_field_theme.dart'; import '../../../widgets/custom_action_popup.dart'; +import 'agent.dart'; import 'agentIncentiveFile.dart'; class AgentList extends ConsumerStatefulWidget { @@ -28,12 +29,14 @@ class AgentListState extends ConsumerState { int itemsPerPage = 10; late ApiService apiService; dynamic managerId; + dynamic selected_id; // List> dataVal = []; List> getAgentData = []; List> originalData = []; List> filteredData = []; bool isLoading = false; dynamic sortedData; + dynamic managerID; @override void initState() { super.initState(); @@ -41,13 +44,19 @@ class AgentListState extends ConsumerState { // getAgentList(); Future.microtask(() { - final id = ref.read(managerIdProvider); - if (id != null) { - getAgentList(id); + managerID = ref.read(managerIdProvider); + if (managerID != null) { + getAgentList(managerID); } }); } + void refresh() { + if (managerID != null) { + getAgentList(managerID); + } + } + // @override // void didChangeDependencies() { // super.didChangeDependencies(); @@ -162,6 +171,20 @@ class AgentListState extends ConsumerState { ]; } + Future showAgent({required String id}) { + print('showAgent : $id'); + return showDialog( + context: context, + builder: (ctx) => Agent( + id: id, + onSubmit: (value) { + debugPrint("New Claims: $value"); + refresh(); + }, + ), + ); + } + @override Widget build(BuildContext context) { managerId = ref.watch(managerIdProvider); @@ -301,6 +324,7 @@ class AgentListState extends ConsumerState { 'Email', 'Phone Number', 'Id', + 'Retention Rate', 'Address', 'Status', ], @@ -310,6 +334,7 @@ class AgentListState extends ConsumerState { "email", "mobile", "agent_code", + "retention_rate", "address", "is_active", ], @@ -318,7 +343,8 @@ class AgentListState extends ConsumerState { SizedBox(width: 10), InkWell( onTap: () { - context.go('/agent/create'); + // context.go('/agent/create'); + showAgent(id: 'Create'); }, child: Container( padding: EdgeInsets.all(5.0), @@ -391,6 +417,13 @@ class AgentListState extends ConsumerState { flex: 1, child: Text('Id', style: _headerStyle), ), + Expanded( + flex: 1, + child: Text( + 'Retention Rate ', + style: _headerStyle, + ), + ), Expanded( flex: 2, child: Text('Address', style: _headerStyle), @@ -511,6 +544,10 @@ class AgentListState extends ConsumerState { flex: 1, child: Text(item['agent_code'] ?? '-', style: _dataBold), ), + Expanded( + flex: 1, + child: Text(item['retention_rate'] ?? '-', style: _dataBold), + ), Expanded( flex: 2, child: Text( @@ -567,8 +604,10 @@ class AgentListState extends ConsumerState { ), onPressed: () { print('EDITStaff - ${item['id']}'); - dynamic id = item['id']; - context.go('/agent/$id'); + selected_id = item['id']; + + showAgent(id: selected_id); + // context.go('/agent/$selected_id'); }, splashRadius: 28, hoverColor: Colors.black12, diff --git a/lib/presentation/screens/UserManagement/POS/pos_list.dart b/lib/presentation/screens/UserManagement/POS/pos_list.dart new file mode 100644 index 0000000..472cef0 --- /dev/null +++ b/lib/presentation/screens/UserManagement/POS/pos_list.dart @@ -0,0 +1,569 @@ +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:nhance_partner/presentation/providers/userRoleProvider.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 '../../../themes/indicators/export_btn.dart'; +import '../../../themes/indicators/filter_btn.dart'; +import '../../../themes/indicators/search_field_theme.dart'; +import '../../../themes/indicators/text_field_theme.dart'; +import '../../../widgets/custom_action_popup.dart'; + +class PosList extends ConsumerStatefulWidget { + const PosList({super.key}); + @override + ConsumerState createState() => PosListState(); +} + +class PosListState extends ConsumerState { + int currentPage = 1; + int itemsPerPage = 10; + late ApiService apiService; + dynamic managerId; + dynamic role; + dynamic prefid; + // List> dataVal = []; + List> getStaffData = []; + List> originalData = []; + List> filteredData = []; + bool isLoading = false; + + @override + void initState() { + super.initState(); + + apiService = ApiService(); + + Future.microtask(() { + final data1 = ref.read(managerIdProvider); + + prefid = data1; + // role = ref.read(userRoleProvider); + // print("E43 => mId: $prefid"); + // print("roleSTAFFLIST: $role"); + if (prefid != null) { + getPosList(prefid); + } + }); + // getPosList(); + } + // + // @override + // void didChangeDependencies() { + // super.didChangeDependencies(); + // final id = ref.watch(managerIdProvider); + // if (id != null) { + // getPosList(id); + // } + // } + + List get _paginatedData { + // Sort descending by id first + final sortedData = [...filteredData] + ..sort((a, b) => int.parse(b['id']) - int.parse(a['id'])); + + if (sortedData.isEmpty) return []; + + // Ensure currentPage is valid + final maxPage = (sortedData.length / itemsPerPage).ceil(); + final safePage = currentPage.clamp(1, maxPage); + + final startIndex = (safePage - 1) * itemsPerPage; + final endIndex = (startIndex + itemsPerPage).clamp(0, sortedData.length); + + return sortedData.sublist(startIndex, endIndex); + } + + // void filterData(String query) { + // print("FilterDAta - $query"); + // setState(() { + // filteredData = getStaffData.where((item) { + // final isActiveStatus = item['is_active'] == "1" ? "active" : "inactive"; + // + // return (item['name'] ?? '-').toLowerCase().contains( + // query.toLowerCase(), + // ) || + // (item['email'] ?? '-').toLowerCase().contains( + // query.toLowerCase(), + // ) || + // (item['mobile'] ?? '-').toLowerCase().contains( + // query.toLowerCase(), + // ) || + // (item['address'] ?? '-').toLowerCase().contains( + // query.toLowerCase(), + // ) || + // (item['emp_id'] ?? item['agent_code'] ?? '-') + // .toLowerCase() + // .contains(query.toLowerCase()) || + // isActiveStatus.contains(query.toLowerCase()); + // }).toList(); + // }); + // } + + void filterData(String query) { + setState(() { + final q = query.toLowerCase(); + + if (q.isEmpty) { + filteredData = getStaffData; + return; + } + + filteredData = getStaffData.where((item) { + final isActiveStatus = item['is_active'] == "1" ? "active" : "inactive"; + + return (item['name'] ?? '').toString().toLowerCase().contains(q) || + (item['email'] ?? '').toString().toLowerCase().contains(q) || + (item['mobile'] ?? '').toString().toLowerCase().contains(q) || + (item['address'] ?? '').toString().toLowerCase().contains(q) || + (item['emp_id'] ?? item['agent_code'] ?? '') + .toString() + .toLowerCase() + .contains(q) || + isActiveStatus.contains(q); + }).toList(); + }); + } + + final TextEditingController _searchStaffController = TextEditingController(); + + Future getPosList(int id) async { + print('E104 => Fns called => $id'); + + final val = 'list'; + setState(() { + isLoading = true; + }); + + try { + final response = await apiService.fetchPosList(id, val); + + if (response['status'] == 200) { + print('E113 => getStaffListData => ${response['data']}'); + setState(() { + 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 _buildPopupMenuActions(BuildContext context, dynamic data) { + return [ + GestureDetector( + onTap: () { + Navigator.pop(context); + print('EDITStaff - ${data['id']}'); + dynamic id = data['id']; + context.go('/pos/$id'); + }, + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Icon(Icons.edit_sharp, color: Color(0xFF319718), size: 18), + SizedBox(width: 10), + Text('Edit'), + ], + ), + ), + ]; + } + + @override + Widget build(BuildContext context) { + managerId = ref.watch(managerIdProvider); + + return MainLayout( + title: "Pos", + body: Container( + width: MediaQuery.of(context).size.width, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Expanded( + child: Container( + // color: Colors.green, + // color: Colors.green.shade50, + width: MediaQuery.of(context).size.width, + // margin: EdgeInsets.all(10.0), + decoration: BoxDecoration( + // color: Colors.white, + borderRadius: BorderRadius.circular(15.0), + ), + + padding: EdgeInsets.all(8.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + // height: 40, + // color: Colors.pink, + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Text( + 'POS', + style: GoogleFonts.poppins( + fontSize: 14, + fontWeight: FontWeight.w500, + ), + ), + Spacer(), + ThemedSearchField( + hintText: 'Search', + // backgroundColor: Color(0xFFF6F8F8), + backgroundColor: Color(0xFFFFFFFF), + txtHeight: 30, + onChanged: filterData, + controller: _searchStaffController, + txtwidth: MediaQuery.of(context).size.width * 0.15, + ), + + SizedBox(width: 10), + ExportBtn( + sheetName: "POS", + fileName: "pos_list", + txt: !ResponsiveLayout.isMobile(context) + ? true + : false, + data: filteredData, + displayHeaders: [ + 'S.No.', + 'Pos Name', + 'Code', + 'Email', + 'Mobile', + 'Bank', + 'Aadhar', + 'PAN', + ], + keys: [ + "sno", // handled internally as i + 1 + "name", + "pos_code", + "email", + "mobile", + "bank_name", + "aadhar", + "pan", + // "is_active", + ], + ), + + // SizedBox(width: 10), + // InkWell( + // onTap: () { + // context.go('/pos/create'); + // }, + // child: Container( + // padding: EdgeInsets.all(4.8), + // decoration: BoxDecoration( + // color: const Color(0xFF2E7D6E), + // borderRadius: BorderRadius.circular(8.0), + // ), + // child: Row( + // mainAxisSize: MainAxisSize.min, + // children: [ + // Tooltip( + // message: 'Create Pos', + // child: Icon( + // Icons.add, + // color: Colors.white, + // size: 18, + // ), + // ), + // ], + // ), + // ), + // ), + ], + ), + ), + SizedBox(height: 5), + Container( + decoration: BoxDecoration( + color: Color(0xFFF1F5F9), + + // color: Color(0xFFEDF6F5), + borderRadius: BorderRadius.circular(6), + ), + padding: const EdgeInsets.symmetric( + vertical: 10, + horizontal: 16, + ), + child: Row( + children: [ + Expanded( + flex: 1, + child: Text('S.No.', style: _headerStyle), + ), + Expanded( + flex: 2, + child: Text('Name', style: _headerStyle), + ), + Expanded( + flex: 2, + child: Text('Code', style: _headerStyle), + ), + Expanded( + flex: 2, + child: Text('Email', style: _headerStyle), + ), + Expanded( + flex: 2, + child: Text('Mobile', style: _headerStyle), + ), + Expanded( + flex: 2, + child: Text('Bank', style: _headerStyle), + ), + Expanded( + flex: 2, + child: Text('Aadhar', style: _headerStyle), + ), + Expanded( + flex: 2, + child: Text('PAN', style: _headerStyle), + ), + + // Expanded( + // flex: 1, + // child: Text('Status', 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: filteredData.length + 1, // +1 for header, +1 for pagination + itemCount: sortedData.length + 1, // +1 for header, +1 for pagination + itemBuilder: (context, index) { + if (index == 0) return _buildHeader(); + // if (index == dataVal.length + 1) + // return _buildPagination(context); + final startIndex = ((currentPage - 1) * itemsPerPage); + // final item = filteredData[index - 1]; + final item = sortedData[index - 1]; + final sno = startIndex + index; + return _buildDataRow(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: Colors.blueGrey, width: 0.15), + ), + // borderRadius: BorderRadius.circular(8), + ), + child: Row( + children: [ + Expanded(flex: 1, child: Text('$sno' ?? '-', style: _dataBold)), + Expanded(flex: 2, child: Text(item['name'] ?? '-', style: _dataBold)), + Expanded( + flex: 2, + child: Text(item['pos_code'] ?? '-', style: _dataBold), + ), + + Expanded( + flex: 2, + child: Text(item['email'] ?? '-', style: _dataBold), + ), + Expanded( + flex: 2, + child: Text(item['mobile'] ?? '-', style: _dataBold), + ), + Expanded( + flex: 2, + child: Text(item['bank_name'] ?? '-', style: _dataBold), + ), + Expanded( + flex: 2, + child: Text(item['aadhar'] ?? '-', style: _dataBold), + ), + Expanded(flex: 2, child: Text(item['pan'] ?? '-', style: _dataBold)), + + // Expanded( + // flex: 1, + // child: Row( + // children: [ + // Container( + // // color: Colors.yellow.shade50, + // child: Transform.scale( + // scale: 0.4, // reduce size (0.7–0.9 works well) + // child: Switch( + // value: item['is_active'] == "1", + // onChanged: (val) { + // setState(() { + // item['is_active'] = val ? "1" : "0"; + // }); + // final response = apiService.updateStatus( + // item['id'], + // val ? "0" : "1", + // 'pos', + // ); + // print("Response - $response"); + // }, + // activeColor: Color(0xFF2E7D6E), // thumb when active + // activeTrackColor: Color(0xFFDCFCE7), // track when active + // // activeColor: Color(0xFF425B5B), // thumb when active + // // activeTrackColor: Color(0xFFB2D8D3), // track when active + // inactiveThumbColor: + // Colors.grey.shade400, // thumb when inactive + // inactiveTrackColor: + // Colors.grey.shade300, // track when inactive + // ), + // ), + // ), + // ], + // ), + // ), + // + // Expanded( + // flex: 1, + // child: item['is_active'] == "1" + // ? Row( + // children: [ + // // GestureDetector( + // // onTap: () { + // // // Navigator.pop(context); + // // print('EDITStaff - ${item['id']}'); + // // dynamic id = item['id']; + // // context.go('/pos/$id'); + // // }, + // // child: Image.asset( + // // "assets/miscellaneous/Edit.png", + // // height: 15, + // // width: 15, + // // ), + // // ), + // Tooltip( + // message: 'Edit', + // child: IconButton( + // icon: Image.asset( + // "assets/miscellaneous/Edit.png", + // height: 12, + // width: 15, + // ), + // onPressed: () { + // print('EDITStaff - ${item['id']}'); + // dynamic id = item['id']; + // context.go('/pos/$id'); + // }, + // splashRadius: 28, + // hoverColor: Colors.black12, + // padding: const EdgeInsets.all(8), + // constraints: const BoxConstraints(), + // ), + // ), + // ], + // ) + // : Row( + // children: [ + // Image.asset( + // "assets/miscellaneous/Edit_muted.png", + // height: 12, + // width: 15, + // ), + // ], + // ), + // ), + ], + ), + ); + } + + static final _dataBold = GoogleFonts.inter( + fontSize: 12, + + fontWeight: FontWeight.w400, + color: Color(0xFF000000), + ); + + static final _dataSub = GoogleFonts.inter( + fontSize: 10, + fontWeight: FontWeight.w300, + color: Color(0xFF585757), + ); + + static final _headerStyle = GoogleFonts.poppins( + fontSize: 11.2, + fontWeight: FontWeight.w500, + color: Color(0xFF1E293B), + ); +} diff --git a/lib/presentation/screens/UserManagement/Profile/profile_web.dart b/lib/presentation/screens/UserManagement/Profile/profile_web.dart index 0614d44..5d7ada5 100644 --- a/lib/presentation/screens/UserManagement/Profile/profile_web.dart +++ b/lib/presentation/screens/UserManagement/Profile/profile_web.dart @@ -240,7 +240,10 @@ class ProfilePopUpState extends ConsumerState { ), const SizedBox(height: 26), if (profileData?['mobile'] != null || - profileData?['mobile'] != '') ...[ + profileData!['mobile'] + .toString() + .trim() + .isNotEmpty) ...[ Row( children: [ const Icon( @@ -258,7 +261,10 @@ class ProfilePopUpState extends ConsumerState { const SizedBox(height: 8), ], if (profileData?['email'] != null || - profileData?['email'] != '') ...[ + profileData!['email'] + .toString() + .trim() + .isNotEmpty) ...[ Row( children: [ const Icon( @@ -276,8 +282,11 @@ class ProfilePopUpState extends ConsumerState { const SizedBox(height: 16), ], - if (profileData?['address'] != null || - profileData?['address'] != '') ...[ + if (profileData?['address'] != null && + profileData!['address'] + .toString() + .trim() + .isNotEmpty) ...[ if ((roleId != 1) && (roleId != 2) && (roleId != 3)) ...[ @@ -304,9 +313,7 @@ class ProfilePopUpState extends ConsumerState { ], ], - if ((roleId != 1) && - (roleId != 2) && - (roleId != 3)) ...[ + if ((roleId == 5)) ...[ // Staff, Handler doesn't have this part Only Manager and agent have Container( padding: const EdgeInsets.all(6), diff --git a/lib/presentation/screens/UserManagement/SalesExecutive/salesExecutive.dart b/lib/presentation/screens/UserManagement/SalesExecutive/salesExecutive.dart index 628dab6..3562719 100644 --- a/lib/presentation/screens/UserManagement/SalesExecutive/salesExecutive.dart +++ b/lib/presentation/screens/UserManagement/SalesExecutive/salesExecutive.dart @@ -27,7 +27,8 @@ import '../../../themes/indicators/input_field_decoration.dart'; class SalesExecutive extends ConsumerStatefulWidget { final String? id; - const SalesExecutive({super.key, this.id}); + final void Function(String value) onSubmit; + const SalesExecutive({super.key, this.id, required this.onSubmit}); @override ConsumerState createState() => SalesExecutiveState(); } @@ -75,7 +76,7 @@ class SalesExecutiveState extends ConsumerState { // "emp_id": controllers["code"]?.text, "is_active": isActive, // "handler_id": selectedHandler, - "manager_id": userId, + "manager_id": managerId, }; return data; } @@ -204,7 +205,9 @@ class SalesExecutiveState extends ConsumerState { context, 'SalesExecutive Created Successfully', ); - context.go(AppRoutes.salesExecutiveLst); + Navigator.of(context).pop(); + widget.onSubmit("success"); + // context.go(AppRoutes.salesExecutiveLst); } else if (response.statusCode == 403) { await apiService.clearLocalStorageAndRedirect(); } else { @@ -310,11 +313,12 @@ class SalesExecutiveState extends ConsumerState { @override Widget build(BuildContext context) { - return MainLayout( - title: "SalesExecutive", - body: Container( + return AlertDialog( + backgroundColor: Colors.white, + content: Container( // color: Colors.yellow.shade50, - width: MediaQuery.of(context).size.width, + width: MediaQuery.of(context).size.width * 0.42, + height: MediaQuery.of(context).size.height * 0.55, child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start, @@ -331,23 +335,23 @@ class SalesExecutiveState extends ConsumerState { crossAxisAlignment: CrossAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.start, children: [ - Tooltip( - message: 'Back', - child: IconButton( - icon: const Icon( - Icons.arrow_left_sharp, - size: 25, - color: Color(0xFF425B5B), - ), - onPressed: () { - context.go(AppRoutes.salesExecutiveLst); - }, - splashRadius: 28, - hoverColor: Colors.black12, - padding: const EdgeInsets.all(8), - constraints: const BoxConstraints(), - ), - ), + // Tooltip( + // message: 'Back', + // child: IconButton( + // icon: const Icon( + // Icons.arrow_left_sharp, + // size: 25, + // color: Color(0xFF425B5B), + // ), + // onPressed: () { + // context.go(AppRoutes.salesExecutiveLst); + // }, + // splashRadius: 28, + // hoverColor: Colors.black12, + // padding: const EdgeInsets.all(8), + // constraints: const BoxConstraints(), + // ), + // ), const SizedBox(width: 5), // spacing between icon and text Text( "Sales Executive", @@ -356,6 +360,21 @@ class SalesExecutiveState extends ConsumerState { fontWeight: FontWeight.w500, ), ), + Spacer(), + 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: Tooltip( + message: 'Close', + child: const Icon(Icons.close, size: 18), + ), + ), + ), ], ), ), @@ -405,7 +424,7 @@ class SalesExecutiveState extends ConsumerState { ), ], ), - SizedBox(height: 30), + // SizedBox(height: 30), ], ), ), @@ -422,51 +441,40 @@ class SalesExecutiveState extends ConsumerState { return Form( key: _formKey, child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, children: [ - Row( - children: [ - Expanded(child: buildName()), - SizedBox(width: 25), - Expanded(child: buildEmail()), - ], - ), - SizedBox(height: 20), - Row( - children: [ - Expanded(child: buildPhNumber()), - SizedBox(width: 25), - Expanded(child: buildAddress()), - ], - ), + Row(children: [buildName(), SizedBox(width: 25), buildEmail()]), SizedBox(height: 20), + Row(children: [buildPhNumber(), SizedBox(width: 25), buildAddress()]), ], ), ); } Widget buildName() { - return Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, + return Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ Text("Name *", style: _textStyle), - SizedBox(width: 10), + SizedBox(height: 10), ThemedFormField( borderColor: Color(0xFFE2E8F0), highlightColor: Color(0xFF50A398), controller: controllers['name']!, validator: (value) => Validators.requiredField(value, "name"), - txtwidth: MediaQuery.of(context).size.width * 0.26, + txtwidth: MediaQuery.of(context).size.width * 0.18, ), ], ); } Widget buildEmail() { - return Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, + return Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ Text("Email *", style: _textStyle), - SizedBox(width: 10), + SizedBox(height: 10), ThemedFormField( controller: controllers['email']!, borderColor: Color(0xFFE2E8F0), @@ -485,18 +493,18 @@ class SalesExecutiveState extends ConsumerState { inputFormatters: [ FilteringTextInputFormatter.allow(RegExp(r'[a-zA-Z0-9_@.]')), ], - txtwidth: MediaQuery.of(context).size.width * 0.26, + txtwidth: MediaQuery.of(context).size.width * 0.18, ), ], ); } Widget buildPhNumber() { - return Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, + return Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ Text("Phone Number *", style: _textStyle), - SizedBox(width: 10), + SizedBox(height: 10), ThemedFormField( controller: controllers['mobile']!, borderColor: Color(0xFFE2E8F0), @@ -516,18 +524,18 @@ class SalesExecutiveState extends ConsumerState { inputFormatters: [ FilteringTextInputFormatter.allow(RegExp(r'[0-9]')), ], - txtwidth: MediaQuery.of(context).size.width * 0.26, + txtwidth: MediaQuery.of(context).size.width * 0.18, ), ], ); } Widget buildAddress() { - return Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, + return Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ Text("Address", style: _textStyle), - SizedBox(width: 10), + SizedBox(height: 10), ThemedFormField( controller: controllers['address']!, borderColor: Color(0xFFE2E8F0), @@ -535,15 +543,15 @@ class SalesExecutiveState extends ConsumerState { inputFormatters: [ FilteringTextInputFormatter.allow(RegExp(r'[ a-zA-Z0-9_#/.]')), ], - txtwidth: MediaQuery.of(context).size.width * 0.26, + txtwidth: MediaQuery.of(context).size.width * 0.18, ), ], ); } Widget buildId() { - return Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, + return Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ Text("SalesExecutive Id ", style: _textStyle), SizedBox(width: 10), @@ -552,7 +560,7 @@ class SalesExecutiveState extends ConsumerState { borderColor: Color(0xFFE2E8F0), highlightColor: Color(0xFF50A398), // validator: (value) => Validators.requiredField(value, "id"), - txtwidth: MediaQuery.of(context).size.width * 0.26, + txtwidth: MediaQuery.of(context).size.width * 0.18, ), ], ); @@ -567,7 +575,7 @@ class SalesExecutiveState extends ConsumerState { // ThemedFormField( // controller: controllers['address']!, // - // txtwidth: MediaQuery.of(context).size.width * 0.26, + // txtwidth: MediaQuery.of(context).size.width *0.18, // ), // ], // ); @@ -590,8 +598,8 @@ class SalesExecutiveState extends ConsumerState { final isReadOnly = widget.id != null; - return Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, + return Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ Text("Role *", style: _textStyle), SizedBox(width: 10), @@ -602,7 +610,7 @@ class SalesExecutiveState extends ConsumerState { ), width: ResponsiveLayout.isMobile(context) ? null - : MediaQuery.of(context).size.width * 0.26, + : MediaQuery.of(context).size.width * 0.18, // height: 40, child: AbsorbPointer( absorbing: isReadOnly, @@ -727,8 +735,8 @@ class SalesExecutiveState extends ConsumerState { // final isReadOnly = widget.id != null; final isReadOnly = false; - return Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, + return Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ Text("Handler Name *", style: _textStyle), SizedBox(width: 10), @@ -739,7 +747,7 @@ class SalesExecutiveState extends ConsumerState { ), width: ResponsiveLayout.isMobile(context) ? null - : MediaQuery.of(context).size.width * 0.26, + : MediaQuery.of(context).size.width * 0.18, // height: 40, child: AbsorbPointer( absorbing: isReadOnly, diff --git a/lib/presentation/screens/UserManagement/SalesExecutive/salesExecutiveList.dart b/lib/presentation/screens/UserManagement/SalesExecutive/salesExecutiveList.dart index e51c24e..765fadd 100644 --- a/lib/presentation/screens/UserManagement/SalesExecutive/salesExecutiveList.dart +++ b/lib/presentation/screens/UserManagement/SalesExecutive/salesExecutiveList.dart @@ -4,6 +4,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:go_router/go_router.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:nhance_partner/presentation/providers/userRoleProvider.dart'; +import 'package:nhance_partner/presentation/screens/UserManagement/SalesExecutive/salesExecutive.dart'; import '../../../../core/routing/routes.dart'; import '../../../../core/services/api_service.dart'; @@ -56,6 +57,12 @@ class SalesExecutiveListState extends ConsumerState { // getSalesExecutiveList(); } + void refresh() { + if (prefid != null && role != null) { + getSalesExecutiveList(prefid, role); + } + } + List get _paginatedData { // Sort descending by id first final sortedData = [...filteredData] @@ -131,6 +138,20 @@ class SalesExecutiveListState extends ConsumerState { } } + Future showSalesExecutive({required String id}) { + print('showSalesEXEC : $id'); + return showDialog( + context: context, + builder: (ctx) => SalesExecutive( + id: id, + onSubmit: (value) { + debugPrint("New onSubmit: $value"); + refresh(); + }, + ), + ); + } + @override Widget build(BuildContext context) { managerId = ref.watch(managerIdProvider); @@ -170,7 +191,7 @@ class SalesExecutiveListState extends ConsumerState { mainAxisAlignment: MainAxisAlignment.start, children: [ Text( - 'Sales Executive List', + 'Sales Executive', style: GoogleFonts.poppins( fontSize: 14, fontWeight: FontWeight.w500, @@ -216,7 +237,8 @@ class SalesExecutiveListState extends ConsumerState { SizedBox(width: 10), InkWell( onTap: () { - context.go('/salesExecutive/create'); + showSalesExecutive(id: 'create'); + // context.go('/salesExecutive/create'); }, child: Container( padding: EdgeInsets.all(4.8), @@ -427,7 +449,9 @@ class SalesExecutiveListState extends ConsumerState { onPressed: () { print('EDITStaff - ${item['id']}'); dynamic id = item['id']; - context.go('/salesExecutive/$id'); + // context.go('/salesExecutive/$id'); + + showSalesExecutive(id: id); }, splashRadius: 28, hoverColor: Colors.black12, diff --git a/lib/presentation/screens/UserManagement/Staff/staff.dart b/lib/presentation/screens/UserManagement/Staff/staff.dart index b7b428c..ec9cac3 100644 --- a/lib/presentation/screens/UserManagement/Staff/staff.dart +++ b/lib/presentation/screens/UserManagement/Staff/staff.dart @@ -27,7 +27,8 @@ import '../../../themes/indicators/input_field_decoration.dart'; class Staff extends ConsumerStatefulWidget { final String? id; - const Staff({super.key, this.id}); + final void Function(String value) onSubmit; + const Staff({super.key, this.id, required this.onSubmit}); @override ConsumerState createState() => StaffState(); } @@ -56,8 +57,8 @@ class StaffState extends ConsumerState { List> filteredRolesData = []; List> getRolesData = []; - List? selectedHandlerIds = []; String? selectedHandler; + List? selectedHandlerIds = []; List> filteredHandlersData = []; List> getHandlersData = []; @@ -109,7 +110,7 @@ class StaffState extends ConsumerState { } void updateData() async { - if (widget.id != null && widget.id != 'create') { + if (widget.id != null && widget.id != 'Create') { dynamic response = await apiService.findSingleStaffData(widget.id!); final data = response['data']; print("updateData - ${response['data']}"); @@ -184,7 +185,7 @@ class StaffState extends ConsumerState { } Future createUserData(data) async { - final bool isUpdating = widget.id != null && widget.id != 'create'; + final bool isUpdating = widget.id != null && widget.id != 'Create'; final String? id = isUpdating ? widget.id : null; final String apiUrldata; // apiUrldata = isUpdating @@ -234,7 +235,10 @@ class StaffState extends ConsumerState { context, 'Staff Created Successfully', ); - context.go(AppRoutes.staffLst); + + Navigator.of(context).pop(); + widget.onSubmit("success"); + // context.go(AppRoutes.staffLst); } else if (response.statusCode == 403) { await apiService.clearLocalStorageAndRedirect(); } else { @@ -340,11 +344,12 @@ class StaffState extends ConsumerState { @override Widget build(BuildContext context) { - return MainLayout( - title: "Staff", - body: Container( + return AlertDialog( + backgroundColor: Colors.white, + content: Container( // color: Colors.yellow.shade50, - width: MediaQuery.of(context).size.width, + width: MediaQuery.of(context).size.width * 0.62, + height: MediaQuery.of(context).size.height * 0.65, child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start, @@ -361,23 +366,23 @@ class StaffState extends ConsumerState { crossAxisAlignment: CrossAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.start, children: [ - Tooltip( - message: 'Back', - child: IconButton( - icon: const Icon( - Icons.arrow_left_sharp, - size: 25, - color: Color(0xFF425B5B), - ), - onPressed: () { - context.go(AppRoutes.staffLst); - }, - splashRadius: 28, - hoverColor: Colors.black12, - padding: const EdgeInsets.all(8), - constraints: const BoxConstraints(), - ), - ), + // Tooltip( + // message: 'Back', + // child: IconButton( + // icon: const Icon( + // Icons.arrow_left_sharp, + // size: 25, + // color: Color(0xFF425B5B), + // ), + // onPressed: () { + // context.go(AppRoutes.staffLst); + // }, + // splashRadius: 28, + // hoverColor: Colors.black12, + // padding: const EdgeInsets.all(8), + // constraints: const BoxConstraints(), + // ), + // ), const SizedBox(width: 5), // spacing between icon and text Text( "Staff", @@ -386,6 +391,21 @@ class StaffState extends ConsumerState { fontWeight: FontWeight.w500, ), ), + Spacer(), + 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: Tooltip( + message: 'Close', + child: const Icon(Icons.close, size: 18), + ), + ), + ), ], ), ), @@ -455,30 +475,30 @@ class StaffState extends ConsumerState { children: [ Row( children: [ - Expanded(child: buildName()), + buildName(), SizedBox(width: 25), - Expanded(child: buildEmail()), + buildEmail(), + SizedBox(width: 25), + buildPhNumber(), ], ), SizedBox(height: 20), Row( children: [ - Expanded(child: buildPhNumber()), + buildRole(context), SizedBox(width: 25), - Expanded(child: buildRole(context)), + if (showHandler) ...[ + Row( + children: [ + buildHandler(context), + SizedBox(width: 25), + SizedBox.shrink(), + ], + ), + ], ], ), - SizedBox(height: 20), - if (showHandler) ...[ - Row( - children: [ - Expanded(child: buildHandler(context)), - SizedBox(width: 25), - Expanded(child: SizedBox.shrink()), - ], - ), - ], // Row( // children: [ // Expanded(child: buildAddress()), @@ -492,28 +512,28 @@ class StaffState extends ConsumerState { } Widget buildName() { - return Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, + return Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ Text("Full Name *", style: _textStyle), - SizedBox(width: 10), + SizedBox(height: 10), ThemedFormField( borderColor: Color(0xFFE2E8F0), highlightColor: Color(0xFF50A398), controller: controllers['name']!, validator: (value) => Validators.requiredField(value, "name"), - txtwidth: MediaQuery.of(context).size.width * 0.26, + txtwidth: MediaQuery.of(context).size.width * 0.18, ), ], ); } Widget buildEmail() { - return Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, + return Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ Text("Email *", style: _textStyle), - SizedBox(width: 10), + SizedBox(height: 10), ThemedFormField( controller: controllers['email']!, borderColor: Color(0xFFE2E8F0), @@ -532,18 +552,18 @@ class StaffState extends ConsumerState { inputFormatters: [ FilteringTextInputFormatter.allow(RegExp(r'[a-zA-Z0-9_@.]')), ], - txtwidth: MediaQuery.of(context).size.width * 0.26, + txtwidth: MediaQuery.of(context).size.width * 0.18, ), ], ); } Widget buildPhNumber() { - return Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, + return Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ Text("Phone Number *", style: _textStyle), - SizedBox(width: 10), + SizedBox(height: 10), ThemedFormField( controller: controllers['mobile']!, borderColor: Color(0xFFE2E8F0), @@ -563,24 +583,24 @@ class StaffState extends ConsumerState { inputFormatters: [ FilteringTextInputFormatter.allow(RegExp(r'[0-9]')), ], - txtwidth: MediaQuery.of(context).size.width * 0.26, + txtwidth: MediaQuery.of(context).size.width * 0.18, ), ], ); } Widget buildId() { - return Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, + return Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ Text("Staff Id ", style: _textStyle), - SizedBox(width: 10), + SizedBox(height: 10), ThemedFormField( controller: controllers['code']!, borderColor: Color(0xFFE2E8F0), highlightColor: Color(0xFF50A398), // validator: (value) => Validators.requiredField(value, "id"), - txtwidth: MediaQuery.of(context).size.width * 0.26, + txtwidth: MediaQuery.of(context).size.width * 0.18, ), ], ); @@ -595,7 +615,7 @@ class StaffState extends ConsumerState { // ThemedFormField( // controller: controllers['address']!, // - // txtwidth: MediaQuery.of(context).size.width * 0.26, + // txtwidth: MediaQuery.of(context).size.width * 0.18, // ), // ], // ); @@ -616,13 +636,13 @@ class StaffState extends ConsumerState { showHandler = false; } - final isReadOnly = widget.id != null; + final isReadOnly = widget.id != null && widget.id != 'Create'; - return Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, + return Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ Text("Role *", style: _textStyle), - SizedBox(width: 10), + SizedBox(height: 10), Container( decoration: BoxDecoration( // color: Colors.white, @@ -630,7 +650,7 @@ class StaffState extends ConsumerState { ), width: ResponsiveLayout.isMobile(context) ? null - : MediaQuery.of(context).size.width * 0.26, + : MediaQuery.of(context).size.width * 0.18, // height: 40, child: AbsorbPointer( absorbing: isReadOnly, @@ -719,13 +739,14 @@ class StaffState extends ConsumerState { // constraints: BoxConstraints(), ), - onChanged: widget.id != null + onChanged: widget.id != null && widget.id != 'Create' ? null : (val) { if (val != null) { print("Selected Role : ${val['role']}"); print("Id: ${val['id']}"); selectedRole = val['id']; + print('selectedRole - ${val['role']}'); if (val['role'] == 'Staff') { setState(() { showHandler = true; @@ -736,6 +757,8 @@ class StaffState extends ConsumerState { }); } + print('showHandler - $showHandler'); + // controllers['agentId']?.text = val['agent_code']; // agentId = agent['id']; } @@ -755,11 +778,11 @@ class StaffState extends ConsumerState { // final isReadOnly = widget.id != null; final isReadOnly = false; - return Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, + return Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ Text("Handler Name *", style: _textStyle), - SizedBox(width: 10), + SizedBox(height: 10), Container( decoration: BoxDecoration( // color: Colors.white, @@ -767,7 +790,7 @@ class StaffState extends ConsumerState { ), width: ResponsiveLayout.isMobile(context) ? null - : MediaQuery.of(context).size.width * 0.26, + : MediaQuery.of(context).size.width * 0.18, // height: 40, child: AbsorbPointer( absorbing: isReadOnly, diff --git a/lib/presentation/screens/UserManagement/Staff/staffList.dart b/lib/presentation/screens/UserManagement/Staff/staffList.dart index b875b87..783425d 100644 --- a/lib/presentation/screens/UserManagement/Staff/staffList.dart +++ b/lib/presentation/screens/UserManagement/Staff/staffList.dart @@ -4,6 +4,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:go_router/go_router.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:nhance_partner/presentation/providers/userRoleProvider.dart'; +import 'package:nhance_partner/presentation/screens/UserManagement/Staff/staff.dart'; import '../../../../core/routing/routes.dart'; import '../../../../core/services/api_service.dart'; @@ -56,6 +57,13 @@ class StaffListState extends ConsumerState { }); // getStaffList(); } + + void refresh() { + if (prefid != null && role != null) { + getStaffList(prefid, role); + } + } + // // @override // void didChangeDependencies() { @@ -167,6 +175,20 @@ class StaffListState extends ConsumerState { } } + Future showStaff({required String id}) { + print('showAgent : $id'); + return showDialog( + context: context, + builder: (ctx) => Staff( + id: id, + onSubmit: (value) { + debugPrint("New Claims: $value"); + refresh(); + }, + ), + ); + } + List _buildPopupMenuActions(BuildContext context, dynamic data) { return [ GestureDetector( @@ -204,48 +226,6 @@ class StaffListState extends ConsumerState { crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start, children: [ - // Container( - // // height: 30, - // // color: Colors.red.shade50, - // width: MediaQuery.of(context).size.width, - // child: GestureDetector( - // onTap: () { - // context.go(AppRoutes.dashboard); - // }, - // child: Row( - // crossAxisAlignment: CrossAxisAlignment.center, - // mainAxisAlignment: MainAxisAlignment.start, - // children: [ - // Text('Staff List', style: _headerStyle), - // // Icon( - // // Icons.arrow_left_sharp, - // // size: 35, - // // color: Color(0xFF425B5B), - // // ), - // // Tooltip( - // // message: 'Back', - // // child: IconButton( - // // icon: const Icon( - // // Icons.arrow_left_sharp, - // // size: 25, - // // color: Color(0xFF425B5B), - // // ), - // // onPressed: () { - // // context.go(AppRoutes.dashboard); - // // }, - // // splashRadius: 18, - // // hoverColor: Colors.black12, - // // padding: const EdgeInsets.all(4), - // // constraints: const BoxConstraints(), - // // ), - // // ), - // // const SizedBox(width: 15), // spacing between icon and text - // ], - // ), - // ), - // ), - // - // SizedBox(height: 5), Expanded( child: Container( // color: Colors.green, @@ -269,7 +249,7 @@ class StaffListState extends ConsumerState { mainAxisAlignment: MainAxisAlignment.start, children: [ Text( - 'Staff List', + 'Staff', style: GoogleFonts.poppins( fontSize: 14, fontWeight: FontWeight.w500, @@ -315,7 +295,9 @@ class StaffListState extends ConsumerState { SizedBox(width: 10), InkWell( onTap: () { - context.go('/staff/create'); + // context.go('/staff/create'); + + showStaff(id: 'Create'); }, child: Container( padding: EdgeInsets.all(4.8), @@ -544,7 +526,9 @@ class StaffListState extends ConsumerState { onPressed: () { print('EDITStaff - ${item['id']}'); dynamic id = item['id']; - context.go('/staff/$id'); + + showStaff(id: id); + // context.go('/staff/$id'); }, splashRadius: 28, hoverColor: Colors.black12, diff --git a/lib/presentation/screens/dashboard/tabs/bussiness.dart b/lib/presentation/screens/dashboard/tabs/bussiness.dart index 6f589ce..fca0d93 100644 --- a/lib/presentation/screens/dashboard/tabs/bussiness.dart +++ b/lib/presentation/screens/dashboard/tabs/bussiness.dart @@ -243,6 +243,12 @@ class _BussinessDashboardState extends ConsumerState { }).toList(); } + List buildBrokerShortNames() { + return filteredBrokerWise.map((item) { + return item['broker_short_name']?.toString() ?? ''; + }).toList(); + } + List buildProductBarData() { double parseDouble(dynamic value) { if (value == null) return 0.0; @@ -291,8 +297,9 @@ class _BussinessDashboardState extends ConsumerState { child: Column( children: [ Row( - mainAxisAlignment: MainAxisAlignment.center, + // mainAxisAlignment: MainAxisAlignment.center, children: [ + Spacer(), Text('Hide and Show Charts : ', style: _styleSmall1), Row( @@ -355,19 +362,43 @@ class _BussinessDashboardState extends ConsumerState { ), ], ), + Spacer(), + legendWidget(), ], ), - - Expanded( - child: Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - showInsurer ? Expanded(child: insurerContainer()) : SizedBox(), - showBroker ? Expanded(child: brokerContainer()) : SizedBox(), - showProduct ? Expanded(child: productContainer()) : SizedBox(), - ], + (showInsurer || showBroker) + ? Expanded( + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // if (showInsurer) Expanded(child: insurerContainer()), + // if (showBroker) Expanded(child: brokerContainer()), + showInsurer + ? Expanded(child: insurerContainer()) + : SizedBox(), + showBroker + ? Expanded(child: brokerContainer()) + : SizedBox(), + // showProduct ? Expanded(child: productContainer()) : SizedBox(), + ], + ), + ) + : SizedBox(), + if (showProduct) ...[ + SizedBox(height: 10), + Expanded( + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + showProduct + ? Expanded(child: productContainer()) + : SizedBox(), + ], + ), ), - ), + ] else ...[ + SizedBox.shrink(), + ], ], ), ); @@ -381,31 +412,18 @@ class _BussinessDashboardState extends ConsumerState { return Card( margin: const EdgeInsets.only(right: 10), color: Colors.white, - elevation: 1, // card shadow + // color: Colors.amber.shade100, + elevation: 0.01, // card shadow shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)), clipBehavior: Clip.antiAlias, child: Padding( - padding: const EdgeInsets.all(10), + padding: const EdgeInsets.all(4), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text('Insurer Wise', style: _chartHeader), - Text( - '(Previous / Current Month)', - style: _chartHeader.copyWith( - color: Colors.black87, - fontSize: 8, - ), - ), - ], - ), - ), + Expanded(child: Text('Insurer-Wise', style: _chartHeader)), // buildInsurer(context), Text( @@ -425,6 +443,7 @@ class _BussinessDashboardState extends ConsumerState { SizedBox(height: 10), Expanded( child: Container( + // color: Colors.amber.shade100, clipBehavior: Clip.none, // color: Colors.red.shade100, child: CustomBarChart( @@ -433,7 +452,7 @@ class _BussinessDashboardState extends ConsumerState { ), ), ), - legendWidget(), + // legendWidget(), ], ), ), @@ -443,27 +462,23 @@ class _BussinessDashboardState extends ConsumerState { Widget brokerContainer() { final chartBrokerData = buildBrokerBarData(); final chartBrokerLabels = buildBrokerLabels(); + final chartBrokerShortName = buildBrokerShortNames(); print('chartBrokerData - $chartBrokerData'); print('chartBrokerLabels - $chartBrokerLabels'); return Card( margin: const EdgeInsets.only(right: 10), color: Colors.white, - elevation: 1, // card shadow + elevation: 0.01, // card shadow shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)), clipBehavior: Clip.antiAlias, child: Padding( - padding: const EdgeInsets.all(10), + padding: const EdgeInsets.all(4), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ - Expanded( - child: Text( - 'Broker Wise (Previous / Current Month)', - style: _chartHeader, - ), - ), + Expanded(child: Text('Broker-Wise', style: _chartHeader)), Text( isBrokerWisePolicy ? "Policies" : "Premium", @@ -483,10 +498,14 @@ class _BussinessDashboardState extends ConsumerState { Expanded( child: CustomBarChart( dataList: chartBrokerData, - labels: chartBrokerLabels, + labels: chartBrokerShortName, + shortName: chartBrokerLabels, + + // labels: chartBrokerLabels, + // shortName: chartBrokerShortName, ), ), - legendWidget(), + // legendWidget(), ], ), ), @@ -501,22 +520,17 @@ class _BussinessDashboardState extends ConsumerState { return Card( margin: const EdgeInsets.only(right: 10), color: Colors.white, - elevation: 1, // card shadow + elevation: 0.01, // card shadow shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)), clipBehavior: Clip.antiAlias, child: Padding( - padding: const EdgeInsets.all(10.0), + padding: const EdgeInsets.all(4.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ - Expanded( - child: Text( - 'Product Wise (Previous / Current Month)', - style: _chartHeader, - ), - ), + Expanded(child: Text('Product-Wise', style: _chartHeader)), Text( isProductWisePolicy ? "Policies" : "Premium", style: _styleSmall1, @@ -538,7 +552,7 @@ class _BussinessDashboardState extends ConsumerState { labels: chartProductLabels, ), ), - legendWidget(), + // legendWidget(), ], ), ), diff --git a/lib/presentation/screens/dashboard/tabs/partner.dart b/lib/presentation/screens/dashboard/tabs/partner.dart index 3c73ec6..e17fce2 100644 --- a/lib/presentation/screens/dashboard/tabs/partner.dart +++ b/lib/presentation/screens/dashboard/tabs/partner.dart @@ -372,6 +372,8 @@ class _DashboardState extends ConsumerState { : agentsPerformanceListPremium, isPerfomingAgntPolicy: isPerfomingAgntPolicy, + apiService: apiService, + managerId: managerId, ), ), ], @@ -437,6 +439,8 @@ class _DashboardState extends ConsumerState { : nonAgentsPerformanceListPremium, isPerfomingAgntPolicy: isNonPerfomingAgntPolicy, + apiService: apiService, + managerId: managerId, ), ), ], @@ -459,12 +463,16 @@ class Performance extends StatefulWidget { final String title; final List> data; final bool isPerfomingAgntPolicy; + late ApiService apiService; + dynamic managerId; Performance({ Key? key, required this.title, required this.data, required this.isPerfomingAgntPolicy, + required this.apiService, + required this.managerId, }) : super(key: key); @override @@ -504,7 +512,7 @@ class _PerformanceState extends State { Expanded( flex: 1, child: Text( - "S.no.", + "S.No.", textAlign: TextAlign.left, style: _headerStyle, ), @@ -521,7 +529,7 @@ class _PerformanceState extends State { flex: 1, child: Text( "Partner Code", - textAlign: TextAlign.center, + textAlign: TextAlign.left, style: _headerStyle, ), ), @@ -610,34 +618,45 @@ class _PerformanceState extends State { flex: 1, child: Text( row['total_premium_amount'] ?? "", - textAlign: TextAlign.center, + textAlign: TextAlign.right, style: _tableDataStyle, ), ), ], - // (widget.title == 'Performing Partner') - // ? Expanded( - // flex: 1, - // child: Text( - // isPerfomingAgntPolicy - // ? (row['policy_count']?.toString() ?? "0") - // : (row['total_premium_amount'] - // ?.toString() ?? - // "0"), - // textAlign: TextAlign.center, - // style: GoogleFonts.inter( - // fontSize: 11, - // fontWeight: FontWeight.w500, - // // color: isPerfomingAgntPolicy - // // ? Colors.black - // // : (row['status']?.toString().toLowerCase() == - // // "active" - // // ? Colors.green - // // : Colors.red), - // ), - // ), - // ) - // : SizedBox(), + SizedBox(width: 10), + InkWell( + onTap: () async { + final path1 = 'PerformingTop50'; + final path2 = widget.isPerfomingAgntPolicy + ? "NoBusiness" + : "NonPerformingBelow50K"; + final path = (widget.title == 'Performing Partner') + ? path1 + : path2; + + final id = + ((widget.title == 'Performing Partner') && + (row['agent_id'] != null)) + ? row['agent_id'] + : ''; + final month = 'current'; + + await widget.apiService.generateChartExcel( + path, + id, + month, + widget.managerId, + ); + }, + child: Tooltip( + message: 'Click To Download Excel', + child: Icon( + Icons.download, + size: 14, + color: Colors.green.shade300, + ), + ), + ), ], ), ); diff --git a/lib/presentation/screens/dashboard/tabs/productivity.dart b/lib/presentation/screens/dashboard/tabs/productivity.dart index 6be2d79..8e03f76 100644 --- a/lib/presentation/screens/dashboard/tabs/productivity.dart +++ b/lib/presentation/screens/dashboard/tabs/productivity.dart @@ -48,6 +48,7 @@ class _ProductivityDashboardState extends ConsumerState { []; // 👈 Add this at the top with other state variables Map vehicleColors = {}; List staffLabels = []; + List staffLabelsId = []; // List>> staffChartData = []; List>> staffChartData = []; @@ -170,6 +171,7 @@ class _ProductivityDashboardState extends ConsumerState { print('Dynamic Vehicle Colors: $dynamicVehicleColors'); List newLabels = []; + List newLabelsId = []; List>> newChartData = []; List> vehicleNames = []; double newMax = 0.0; @@ -188,6 +190,9 @@ class _ProductivityDashboardState extends ConsumerState { ''; newLabels.add(name); + final id = (staff['sales_executive_id'])?.toString() ?? ''; + newLabelsId.add(id); + final products = (staff['products_list'] ?? []) as List; List> currentRod = []; @@ -232,6 +237,7 @@ class _ProductivityDashboardState extends ConsumerState { filteredStaffWise = List>.from(originalStaffWise); staffLabels = newLabels; + staffLabelsId = newLabelsId; staffChartData = newChartData; staffVehicleTypeNames = vehicleNames; vehicleColors = dynamicVehicleColors; // Save for legend @@ -274,79 +280,6 @@ class _ProductivityDashboardState extends ConsumerState { }).toList(); } - // In insurerContainer() you are passing staffLabels (which we now populate). - // Also set the chart maxY to some headroom: - // CustomStackedBarChart(..., maxY: (maxPremium * 1.1).ceilToDouble(), ...) - - List buildStaffBarData() { - double parseDouble(dynamic value) { - if (value == null) return 0.0; - if (value is num) return value.toDouble(); - if (value is String) return double.tryParse(value) ?? 0.0; - return 0.0; - } - - return filteredStaffWise.map((item) { - print('policies_current - ${item['total_policies_current_month']}'); - print('policies_PREV - ${item['total_policies_pre_month']}'); - - print('premium_current - ${item['total_premium_current_month']}'); - print('premium_PREV - ${item['total_premium_pre_month']}'); - return BarData( - Colors.blue, - isInsureWisePolicy - ? parseDouble(item['total_policies_current_month']) - : parseDouble(item['total_premium_current_month']), - isInsureWisePolicy - ? parseDouble(item['total_policies_pre_month']) - : parseDouble(item['total_premium_pre_month']), - item['vehicle_type'] != null ? item['vehicle_type'] : 0, - 'Productivity', - 5, - ); - }).toList(); - } - - List buildProductBarData() { - double parseDouble(dynamic value) { - if (value == null) return 0.0; - if (value is num) return value.toDouble(); - if (value is String) return double.tryParse(value) ?? 0.0; - return 0.0; - } - - return filteredProductWise.map((item) { - print('policies_current - ${item['total_policies_current_month']}'); - print('policies_PREV - ${item['total_policies_pre_month']}'); - - print('premium_current - ${item['total_premium_current_month']}'); - print('premium_PREV - ${item['total_premium_pre_month']}'); - return BarData( - Colors.blue, - isProductWisePolicy - ? parseDouble(item['total_policies_current_month']) - : parseDouble(item['total_premium_current_month']), - isProductWisePolicy - ? parseDouble(item['total_policies_pre_month']) - : parseDouble(item['total_premium_pre_month']), - item['vehicle_type'], - 'Productivity', - 10, - ); - }).toList(); - } - - List buildProductLabels() { - return filteredProductWise.map((item) { - final name = item['vehicle_type']; - if (name != null && name.toString().trim().isNotEmpty) { - return name.toString(); - } else { - return item['vehicle_type']?.toString() ?? ''; - } - }).toList(); - } - @override Widget build(BuildContext context) { return Container( @@ -417,9 +350,6 @@ class _ProductivityDashboardState extends ConsumerState { } Widget insurerContainer() { - final chartStaffData = buildStaffBarData(); - final chartStaffLabels = buildStaffLabels(); - return Card( margin: const EdgeInsets.only(right: 10), color: Colors.white, @@ -447,6 +377,10 @@ class _ProductivityDashboardState extends ConsumerState { Transform.scale( scale: 0.5, child: Switch( + activeColor: Colors.teal, + activeTrackColor: Colors.grey.shade200, + inactiveThumbColor: Colors.orange, + inactiveTrackColor: Colors.white, value: isInsureWisePolicy, onChanged: (val) { setState(() { @@ -463,6 +397,7 @@ class _ProductivityDashboardState extends ConsumerState { Expanded( child: CustomStackedBarChart( labels: staffLabels, + labelsId: staffLabelsId, // maxY: (maxPremium * 1.1).ceilToDouble(), maxY: _getSmartMaxValue(), initialRotation: 1, @@ -479,55 +414,6 @@ class _ProductivityDashboardState extends ConsumerState { ); } - // Widget productContainer() { - // final chartProductData = buildProductBarData(); - // final chartProductLabels = buildProductLabels(); - // print('chartProductData - $chartProductData'); - // print('chartProductLabels - $chartProductLabels'); - // return Container( - // padding: EdgeInsets.all(10), - // margin: EdgeInsets.only(right: 20), - // decoration: BoxDecoration( - // color: Colors.white, - // borderRadius: BorderRadius.circular(10), - // ), - // child: Column( - // crossAxisAlignment: CrossAxisAlignment.start, - // children: [ - // Row( - // children: [ - // Expanded( - // child: Text( - // 'Sales Executive Product Wise (Previous / Current Month)', - // style: _chartHeader, - // ), - // ), - // Text( - // isProductWisePolicy ? "Policies" : "Premium", - // style: _styleSmall1, - // ), - // Transform.scale( - // scale: 0.5, - // child: Switch( - // value: isProductWisePolicy, - // onChanged: (val) => setState(() => isProductWisePolicy = val), - // ), - // ), - // ], - // ), - // SizedBox(height: 10), - // Expanded( - // child: CustomBarChart( - // dataList: chartProductData, - // labels: chartProductLabels, - // ), - // ), - // legendWidget(), - // ], - // ), - // ); - // } - Widget legendWidget() { return Wrap( spacing: 8, diff --git a/lib/presentation/screens/login/login_screen.dart b/lib/presentation/screens/login/login_screen.dart index 7fe2f53..4e6a264 100644 --- a/lib/presentation/screens/login/login_screen.dart +++ b/lib/presentation/screens/login/login_screen.dart @@ -344,7 +344,8 @@ class _LoginScreenState extends ConsumerState { if (userRole == 'staff') { context.go(AppRoutes.enquiryForStaff); } else if (userRole == 'Accounts') { - context.go(AppRoutes.payout); + // context.go(AppRoutes.payout); + context.go(AppRoutes.invoiceList); } else { context.go(AppRoutes.dashboard); } diff --git a/lib/presentation/screens/payout/custom_dateRange.dart b/lib/presentation/screens/payout/custom_dateRange.dart new file mode 100644 index 0000000..da2e71f --- /dev/null +++ b/lib/presentation/screens/payout/custom_dateRange.dart @@ -0,0 +1,806 @@ +import 'package:dropdown_search/dropdown_search.dart'; +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/services/api_service.dart'; +import '../../layouts/responsive_layout.dart'; +import '../../providers/manager_provider.dart'; +import '../../providers/userRoleProvider.dart'; +import '../../themes/indicators/date_field_theme.dart'; +import '../../themes/indicators/input_field_decoration.dart'; + +class DateFilterRowPayout extends ConsumerStatefulWidget { + final TextEditingController startController; + final String? selectedBroker; + final List? selectedParnter; + final String? selectedStaffId; + final TextEditingController endController; + final ValueChanged? onBrokerChanged; + final ValueChanged>? onPartnerChanges; + final String? dataFrom; + final VoidCallback onFilter; + final VoidCallback onRefresh; + final GlobalKey formKey; + final bool isMobile; + final role; + final id; + + const DateFilterRowPayout({ + super.key, + required this.startController, + required this.endController, + required this.onFilter, + required this.onRefresh, + this.onBrokerChanged, + this.onPartnerChanges, + required this.selectedStaffId, + required this.selectedParnter, + this.selectedBroker, + required this.formKey, + required this.role, + required this.id, + this.isMobile = false, + this.dataFrom, + }); + + @override + ConsumerState createState() => _DateFilterRowState(); +} + +class _DateFilterRowState extends ConsumerState { + final GlobalKey>> dropDownKey = + GlobalKey>>(); + + final GlobalKey>> dropDownKeyBroker = + GlobalKey>>(); + + final GlobalKey>> + dropDownKeyPartner = GlobalKey>>(); + + late ApiService apiService; + List? selectedPartnerIds = []; + List> getPartnerData = []; + List> filteredPartnerData = []; + + List> getBrokerData = []; + List> filteredBrokerData = []; + + String? selectedStaff; + String? selectedBroker; + String? selectedPartner; + String? selectedStaffName; + dynamic role; + dynamic managerId; + + bool isLoading = false; + bool isLoadingBroker = false; + bool isLoadingAgentList = false; + + @override + void initState() { + super.initState(); + apiService = ApiService(); + + Future.microtask(() { + managerId = ref.watch(managerIdProvider); + final userID = ref.watch(userIdProvider); + role = ref.watch(userRoleProvider); + print("managerId - $managerId"); + if (userID != null && role != null) { + print('hansles'); + getPartnerDetails(userID); + } + + if (managerId != null) { + print('managerId - $managerId'); + getAgentList(managerId); + } + }); + + getBroker(); + } + + Future getBroker() async { + print('getBroker called'); + setState(() { + isLoadingBroker = true; + }); + + try { + final response = await apiService.fetchMasterDropDown('Broker'); + + if (response['status'] == 200) { + print('getBroker - ${response['data']}'); + setState(() { + getBrokerData = List>.from(response['data']); + print('API Data - $getBrokerData'); + + filteredBrokerData = List.from(getBrokerData); + print('originalData - $filteredBrokerData'); + }); + } else { + getBrokerData = []; + filteredBrokerData = []; + } + } catch (e) { + print('Exception occurred: $e'); + } finally { + setState(() { + isLoadingBroker = false; + }); + } + } + + Future getAgentList(id) async { + print('getAgentListData called'); + setState(() { + isLoadingAgentList = true; + }); + + try { + final response = await apiService.fetchAgentNameDropDown(id); + print('getAgentListData called response'); + print('get Agent- ${response['data']}'); + if (response['status'] == 'success') { + print('get Agent- ${response['data']}'); + setState(() { + getPartnerData = List>.from(response['data']); + print('API Data - $getPartnerData'); + + filteredPartnerData = List.from(getPartnerData); + print('originalAgentData - $filteredPartnerData'); + }); + } else { + getPartnerData = []; + filteredPartnerData = []; + } + } catch (e) { + print('Exception occurred: $e'); + } finally { + setState(() { + isLoadingAgentList = false; + }); + } + } + + Future getPartnerDetails(int id) async { + print('getPartnerDetails called By handler'); + setState(() { + isLoading = true; + }); + + try { + // final response = await apiService.fetchStaffUserList(id, role); + final response = await apiService.fetchStaffListForEnquiryAssignDropDown( + managerId, + id, + role, + ); + if (response['status'] == 'success') { + print('getStaffDetails - ${response['data']}'); + setState(() { + getPartnerData = List>.from(response['data']); + print('API Data - $getPartnerData'); + + filteredPartnerData = List.from(getPartnerData); + print('originalData - $filteredPartnerData'); + }); + } else { + getPartnerData = []; + filteredPartnerData = []; + } + } catch (e) { + print('Exception occurred: $e'); + } finally { + setState(() { + isLoading = false; + }); + } + } + + @override + Widget build(BuildContext context) { + final spacing = 5.0; + + List getRowChildren(bool isMobile, double spacing) { + final dateFields = [ + buildStartDate(context), + SizedBox(width: spacing), + buildEndDate(context), + SizedBox(width: spacing), + buildBroker(context), + SizedBox(width: spacing), + buildPartner(context), + SizedBox(width: spacing), + ]; + + final buttons = [ + Padding( + padding: const EdgeInsets.symmetric(vertical: 0.0), + // child: GestureDetector( + // onTap: () { + // if (formKey.currentState!.validate()) onFilter(); + // }, + // child: Icon(Icons.filter_alt_outlined), + // ), + child: Tooltip( + message: 'Filter', + child: IconButton( + icon: const Icon( + Icons.search_rounded, + size: 18, + color: const Color(0xFF94A3B8), + ), + + onPressed: () { + if (widget.formKey.currentState!.validate()) { + widget.onFilter(); + } + }, + ), + ), + ), + Padding( + padding: const EdgeInsets.all(0.0), + // child: GestureDetector(onTap: onRefresh, child: Icon(Icons.refresh)), + child: Tooltip( + message: 'Refresh', + child: IconButton( + onPressed: widget.onRefresh, + icon: const Icon( + Icons.refresh, + size: 18, + color: const Color(0xFF94A3B8), + ), + ), + ), + ), + ]; + + if (isMobile) { + // For mobile: buttons below fields + return [ + ...dateFields, + SizedBox(height: 10), + Row(mainAxisAlignment: MainAxisAlignment.end, children: buttons), + ]; + } else { + // For desktop: buttons inline with fields + return [...dateFields, Spacer(), ...buttons]; + } + } + + return Form( + key: widget.formKey, + child: widget.isMobile + ? Column( + crossAxisAlignment: CrossAxisAlignment.start, + + children: getRowChildren(widget.isMobile, spacing), + ) + : Row( + crossAxisAlignment: CrossAxisAlignment.end, + + children: getRowChildren(widget.isMobile, spacing), + ), + ); + } + + Widget buildStartDate(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('Start Date', style: _textStyle), + SizedBox(height: 5), + SizedBox( + height: 35, + child: ThemedDateField( + hintText: "Select Date", + txtwidth: ResponsiveLayout.isMobile(context) + ? null + : MediaQuery.of(context).size.width * 0.15, + // txtheight: 50, + // backgroundColor: const Color(0xFFECECEC), + // validator: (value) => Validators.requiredField(value, "date"), + controller: widget.startController, + onDateSelected: (date) { + print("Picked Date: $date"); + widget.startController.text = DateFormat( + 'dd-MM-yyyy', + ).format(date); + // controllers['date']?.text = date as String; + Future.microtask(() => widget.onFilter()); + }, + ), + ), + ], + ); + } + + Widget buildEndDate(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('End Date', style: _textStyle), + SizedBox(height: 5), + SizedBox( + height: 35, + child: ThemedDateField( + hintText: "Select Date", + txtwidth: ResponsiveLayout.isMobile(context) + ? null + : MediaQuery.of(context).size.width * 0.15, + // txtheight: 50, + // backgroundColor: const Color(0xFFECECEC), + validator: (value) { + final fromText = widget.startController.text ?? ''; + if (fromText.isNotEmpty) { + final startDate = DateFormat('dd-MM-yyyy').parse(fromText); + if (value == null || value.isEmpty) { + return "End Date is required"; + } + final endDate = DateFormat('dd-MM-yyyy').parse(value); + + if (endDate.isBefore(startDate)) { + return "End Date cannot be earlier than Start Date"; + } + } + return null; // ✅ no error + }, + controller: widget.endController, + lastDate: DateTime.now(), + onDateSelected: (date) { + print("Picked Date: $date"); + widget.endController.text = DateFormat('dd-MM-yyyy').format(date); + Future.microtask(() => widget.onFilter()); + // controllers['date']?.text = date as String; + }, + ), + ), + ], + ); + } + + Widget buildBroker(BuildContext context) { + Map? selectedBrokers = filteredBrokerData.firstWhere( + (item) => item['id'].toString() == widget.selectedBroker, + orElse: () => {}, + ); + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('Broker', style: _textStyle), + SizedBox(height: 5), + SizedBox( + height: 35, + width: MediaQuery.of(context).size.width * 0.15, + child: DropdownSearch>( + key: dropDownKeyBroker, + selectedItem: selectedBrokers.isNotEmpty ? selectedBrokers : null, + items: (filter, infiniteScrollProps) { + return filteredBrokerData; + }, + + itemAsString: (val) => val['name'].toString(), // what to show + compareFn: (item, selectedItem) => + item['id'] == selectedItem['id'], // ✅ compare by id + // validator: (val) { + // if (val == null) { + // return "Required"; // ✅ error message + // } + // return null; + // }, + suffixProps: DropdownSuffixProps( + // make sure the dropdown button is visible + dropdownButtonProps: DropdownButtonProps( + isVisible: true, + padding: EdgeInsets.zero, // remove default padding + constraints: const BoxConstraints( + // shrink icon tap area + minWidth: 12, + minHeight: 12, + ), + iconSize: 15, // smaller icon + // icon: const Icon(Icons.arrow_drop_down), + ), + ), + dropdownBuilder: (context, selectedItem) => Align( + alignment: Alignment.centerLeft, + child: Text( + selectedItem != null ? selectedItem['name'].toString() : "", + style: GoogleFonts.poppins( + fontSize: 11, + color: Colors.black, + // color: Color(0XFF6366F1), + ), + overflow: TextOverflow.ellipsis, + maxLines: 1, + softWrap: false, + ), + ), + decoratorProps: DropDownDecoratorProps( + decoration: + AppInputDecorations.dropdownDecoration( + label: "Broker", + ).copyWith( + filled: true, + fillColor: + Colors.white, // 👈 makes the dropdown input white + isDense: true, + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(5), + borderSide: const BorderSide( + color: Color(0xFFE2E8F0), + width: 0.1, + ), + ), + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(5), + borderSide: const BorderSide( + color: Color(0xFFE2E8F0), + width: 0.5, + ), + ), + contentPadding: EdgeInsets.symmetric( + horizontal: 8, + vertical: 6, + ), + ), + ), + + popupProps: PopupProps.menu( + fit: FlexFit.loose, + constraints: BoxConstraints(maxHeight: 250), + menuProps: MenuProps( + backgroundColor: + Colors.white, // 👈 sets dropdown background to white + ), + showSearchBox: true, + searchFieldProps: TextFieldProps( + autofocus: true, + style: GoogleFonts.inter(fontSize: 11, color: Colors.black), + decoration: InputDecoration( + contentPadding: EdgeInsets.all(1), + filled: true, + fillColor: Colors.white, + hintText: "Search Broker...", + hintStyle: GoogleFonts.inter( + fontSize: 12, + color: Colors.black, + ), + enabledBorder: OutlineInputBorder( + borderSide: BorderSide( + color: Colors.white, + ), // 👈 Normal border + ), + focusedBorder: OutlineInputBorder( + borderSide: BorderSide( + color: Colors.white, + width: 1.5, + ), // 👈 Focused border + ), + ), + ), + + itemBuilder: (context, item, isDisabled, isSelected) { + return Container( + // color: isSelected ? Colors.blue.withOpacity(0.1) : null, + padding: const EdgeInsets.symmetric( + horizontal: 8, + vertical: 3, + ), + child: Text( + item['name'].toString(), + style: GoogleFonts.inter(fontSize: 12, color: Colors.black), + ), + ); + }, + // constraints: BoxConstraints(), + ), + + onChanged: (val) { + if (val != null) { + print("Selected Broker : ${val['name']}"); + print("Id: ${val['id']}"); + // selectedBroker = val['id']; + widget.onBrokerChanged?.call(val['id'].toString()); + // widget.onBrokerChanged?.call(val['id']); + // controllers['agentId']?.text = val['agent_code']; + // agentId = agent['id']; + } + }, + ), + ), + ], + ); + } + + Widget buildPartner(BuildContext context) { + final isReadOnly = false; + + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text("Referer", style: _textStyle), + SizedBox(height: 5), + SizedBox( + height: 35, + width: MediaQuery.of(context).size.width * 0.42, + child: DropdownSearch>.multiSelection( + key: dropDownKeyPartner, + + // selectedItem: selectedHandlered.isNotEmpty + // ? selectedHandlered + // : null, + selectedItems: filteredPartnerData + .where( + (item) => (selectedPartnerIds ?? []).contains(item['id']), + ) + .toList(), + items: (filter, infiniteScrollProps) { + return filteredPartnerData; + }, + + itemAsString: (val) => val['name'].toString(), // what to show + compareFn: (item, selectedItem) => + item['id'] == selectedItem['id'], // ✅ compare by id + // validator: (val) { + // if (val == null) { + // return "Required"; // ✅ error message + // } + // return null; + // }, + validator: (val) { + if (val == null || val.isEmpty) { + return "Required"; + } + return null; + }, + + decoratorProps: DropDownDecoratorProps( + decoration: + AppInputDecorations.dropdownDecoration( + label: "Select Partner", + ).copyWith( + hintStyle: GoogleFonts.inter( + fontSize: 12, + color: Colors.black, + ), + filled: true, + fillColor: + Colors.white, // 👈 makes the dropdown input white + enabledBorder: OutlineInputBorder( + borderSide: BorderSide( + color: Color(0xFFE2E8F0), + // color: Colors.white, + ), // 👈 Normal border + ), + focusedBorder: OutlineInputBorder( + borderSide: BorderSide( + color: Color(0xFFE2E8F0), + // color: Colors.white, + width: 1.5, + ), // 👈 Focused border + ), + + contentPadding: EdgeInsets.symmetric( + horizontal: 8, + vertical: 6, + ), + ), + ), + popupProps: PopupPropsMultiSelection.menu( + fit: FlexFit.loose, + constraints: BoxConstraints(maxHeight: 250), + showSearchBox: true, + menuProps: MenuProps(backgroundColor: Colors.white), + + searchFieldProps: TextFieldProps( + autofocus: true, + decoration: InputDecoration( + filled: true, + fillColor: Colors.white, + hintText: "Search Partner...", + hintStyle: GoogleFonts.inter( + fontSize: 11, + color: Colors.black, + ), + + enabledBorder: OutlineInputBorder( + borderSide: BorderSide(color: Colors.white), + ), + focusedBorder: OutlineInputBorder( + borderSide: BorderSide( + // color: Colors.blue, + color: Color(0xFFEDF6F5), + width: 1.5, + ), + ), + ), + ), + + itemBuilder: (context, item, isDisabled, isSelected) { + return Container( + // color: isSelected ? Colors.blue.withOpacity(0.1) : null, + padding: const EdgeInsets.symmetric( + horizontal: 8, + vertical: 3, + ), + child: Text( + item['name'].toString(), + style: GoogleFonts.inter(fontSize: 12, color: Colors.black), + ), + ); + }, + ), + + onChanged: (List> selectedVals) { + // selectedPartnerIds = selectedVals + // .map((v) => v['id'].toString()) + // .toList(); + final ids = selectedVals.map((v) => v['id'].toString()).toList(); + print("Selected PArnter IDs: $selectedPartnerIds"); + widget.onPartnerChanges?.call(ids); + // widget.onPartnerChanges!(v['id']); + }, + ), + ), + ], + ); + } + + Widget buildSelectStaffMem(BuildContext context) { + Map? selectedVehicle; + if (widget.selectedStaffId == null) { + selectedVehicle = null; + } else if (widget.selectedStaffId != null) { + selectedVehicle = filteredPartnerData.firstWhere( + (item) => item['id'] == widget.selectedStaffId, + orElse: () => {}, // empty map + ); + if (selectedVehicle.isEmpty) selectedVehicle = null; + } else if (selectedStaff != null) { + selectedVehicle = filteredPartnerData.firstWhere( + (item) => item['id'] == selectedStaff, + orElse: () => {}, // empty map + ); + if (selectedVehicle.isEmpty) selectedVehicle = null; + } else { + selectedVehicle = null; + } + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // Text("Staff Name", style: _textStyle), + // SizedBox(height: 10), + SizedBox( + height: 35, + child: Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(5.0), + border: Border.all(color: Color(0xFFE2E8F0)), + // color: Colors.black, + ), + + width: ResponsiveLayout.isMobile(context) + ? null + : MediaQuery.of(context).size.width * 0.13, + // height: 45, + child: DropdownSearch>( + // key: dropDownKey, + key: ValueKey(selectedStaff), + // selectedItem: selectedVehicle.isNotEmpty ? selectedVehicle : null, + selectedItem: selectedVehicle, + + items: (filter, infiniteScrollProps) { + return filteredPartnerData; + }, + + itemAsString: (val) => val['name'].toString(), + compareFn: (item, selectedItem) => + item['id'] == selectedItem['id'], // ✅ compare by id + dropdownBuilder: (context, selectedItem) => Align( + alignment: Alignment.centerLeft, + child: Text( + selectedItem != null ? selectedItem['name'].toString() : "", + style: GoogleFonts.poppins(fontSize: 11, color: Colors.black), + overflow: TextOverflow.ellipsis, + maxLines: 1, + softWrap: false, + ), + ), + decoratorProps: DropDownDecoratorProps( + decoration: + AppInputDecorations.dropdownDecoration( + label: "Select Staff ", + ).copyWith( + filled: true, + fillColor: + Colors.white, // 👈 makes the dropdown input white + contentPadding: const EdgeInsets.symmetric( + horizontal: 6, + vertical: 1, // 👈 adjust this to make the field shorter + ), + ), + ), + + popupProps: PopupProps.menu( + fit: FlexFit.loose, + constraints: BoxConstraints(maxHeight: 250), + menuProps: MenuProps( + backgroundColor: + Colors.white, // 👈 sets dropdown background to white + ), + showSearchBox: true, + searchFieldProps: TextFieldProps( + decoration: InputDecoration( + filled: true, + fillColor: Colors.white, + hintText: "Search Staff ...", + hintStyle: GoogleFonts.inter( + fontSize: 12, + color: Colors.black, + ), + enabledBorder: OutlineInputBorder( + borderSide: BorderSide( + color: Colors.white, + ), // 👈 Normal border + ), + focusedBorder: OutlineInputBorder( + borderSide: BorderSide( + color: Colors.white, + width: 1.5, + ), // 👈 Focused border + ), + ), + ), + + itemBuilder: (context, item, isDisabled, isSelected) { + return Container( + // color: isSelected ? Colors.blue.withOpacity(0.1) : null, + padding: const EdgeInsets.symmetric( + horizontal: 8, + vertical: 3, + ), + child: Text( + item['name'].toString(), + style: GoogleFonts.inter( + fontSize: 13, + color: Colors.black, + ), + ), + ); + }, + + // constraints: BoxConstraints(), + ), + + onChanged: (val) { + if (val != null) { + print("Selected Staff : ${val['name']}"); + print("Id: ${val['id']}"); + // selectedStaffName = val['name']; + // selectedStaff = val['id']; + widget.onBrokerChanged!(val['id']); + // if (widget.onFilterStaff != null) + // widget.onFilterStaff!(val['id']); + + // Future.microtask(() => widget.onFilter()); + // controllers['agentId']?.text = val['agent_code']; + // agentId = agent['id']; + } + }, + ), + ), + ), + ], + ); + } + + static final _textStyle = GoogleFonts.poppins( + fontSize: 11, + fontWeight: FontWeight.w500, + ); +} diff --git a/lib/presentation/screens/payout/invoice_list.dart b/lib/presentation/screens/payout/invoice_list.dart index d3a4191..5f54c9d 100644 --- a/lib/presentation/screens/payout/invoice_list.dart +++ b/lib/presentation/screens/payout/invoice_list.dart @@ -177,13 +177,16 @@ class _InvoiceListState extends ConsumerState { (item['invoice_no'] ?? '-').toLowerCase().contains( query.toLowerCase(), ) || - (item['invoice_amount'] ?? '-').toLowerCase().contains( - query.toLowerCase(), - ) || + (item['invoice_amount_indian_format'] ?? '-') + .toLowerCase() + .contains(query.toLowerCase()) || (item['broker_name'] ?? '-').toLowerCase().contains( query.toLowerCase(), ) || - (item['agent_name'] ?? '-').toLowerCase().contains( + (item['pos_name'] ?? '-').toLowerCase().contains( + query.toLowerCase(), + ) || + (item['payout_status'] ?? '-').toLowerCase().contains( query.toLowerCase(), ) // || @@ -195,7 +198,7 @@ class _InvoiceListState extends ConsumerState { final TextEditingController _searchStaffController = TextEditingController(); - String _formatDate(String rawDate) { + String _formatDateTime(String rawDate) { try { final dateTime = DateTime.parse(rawDate); return DateFormat('dd-MM-yyyy HH:mm').format(dateTime); // 24-hour format @@ -204,61 +207,69 @@ class _InvoiceListState extends ConsumerState { } } + String _formatDate(String rawDate) { + try { + final dateTime = DateTime.parse(rawDate); + return DateFormat('dd-MM-yyyy').format(dateTime); // 24-hour format + } catch (e) { + return rawDate; // fallback if parsing fails + } + } + @override Widget build(BuildContext context) { return MainLayout( title: "Pay Out", body: Container( - height: 30, + // height: 30, // color: Colors.yellow.shade50, width: MediaQuery.of(context).size.width, child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start, children: [ - Container( - height: 30, - width: MediaQuery.of(context).size.width, - child: GestureDetector( - onTap: () { - context.go(AppRoutes.dashboard); - }, - child: Row( - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisAlignment: MainAxisAlignment.start, - children: [ - // Tooltip( - // message: 'Back', - // child: IconButton( - // icon: const Icon( - // Icons.arrow_left_sharp, - // size: 25, - // color: Color(0xFF425B5B), - // ), - // onPressed: () { - // context.go(AppRoutes.dashboard); - // }, - // splashRadius: 18, - // hoverColor: Colors.black12, - // padding: const EdgeInsets.all(4), - // constraints: const BoxConstraints(), - // ), - // ), - // const SizedBox(width: 5), // spacing between icon and text - Text( - "Invoice", - style: GoogleFonts.inter( - fontSize: 14, - fontWeight: FontWeight.w500, - ), - ), - ], - ), - ), - ), - - SizedBox(height: 5), - + // Container( + // height: 30, + // width: MediaQuery.of(context).size.width, + // child: GestureDetector( + // onTap: () { + // context.go(AppRoutes.dashboard); + // }, + // child: Row( + // crossAxisAlignment: CrossAxisAlignment.center, + // mainAxisAlignment: MainAxisAlignment.start, + // children: [ + // // Tooltip( + // // message: 'Back', + // // child: IconButton( + // // icon: const Icon( + // // Icons.arrow_left_sharp, + // // size: 25, + // // color: Color(0xFF425B5B), + // // ), + // // onPressed: () { + // // context.go(AppRoutes.dashboard); + // // }, + // // splashRadius: 18, + // // hoverColor: Colors.black12, + // // padding: const EdgeInsets.all(4), + // // constraints: const BoxConstraints(), + // // ), + // // ), + // // const SizedBox(width: 5), // spacing between icon and text + // Text( + // "Invoice", + // style: GoogleFonts.inter( + // fontSize: 14, + // fontWeight: FontWeight.w500, + // ), + // ), + // ], + // ), + // ), + // ), + // + // SizedBox(height: 5), Expanded( child: Container( width: MediaQuery.of(context).size.width, @@ -306,71 +317,15 @@ class _InvoiceListState extends ConsumerState { crossAxisAlignment: CrossAxisAlignment.end, mainAxisAlignment: MainAxisAlignment.end, children: [ - // if (!ResponsiveLayout.isMobile(context)) ...[ - // DateFilterRow( - // dataFrom: 'Policy', - // role: roleId, - // id: userId, - // onFilterStaff: (val) { - // print('Selected Filterd STAFF Id - $val'); - // SelectedStaffId = val; - // }, - // selectedStaffId: SelectedStaffId, - // startController: controllers['startDate']!, - // endController: controllers['endDate']!, - // onStatusChanged: (val) { - // SelectedStatus = val; // update parent - // }, - // formKey: _formKey, - // isMobile: ResponsiveLayout.isMobile(context), - // onFilter: () { - // // call your filter logic - // filterDateRange(); - // }, - // onRefresh: () { - // // call your refresh logic - // refrshfilterDateRange(); - // }, - // ), - // - // // Form( - // // key: _formKey, - // // child: Row( - // // mainAxisAlignment: MainAxisAlignment.start, - // // crossAxisAlignment: CrossAxisAlignment.end, - // // children: [ - // // buildStartDate(context), - // // SizedBox(width: 10), - // // buildEndDate(context), - // // SizedBox(width: 10), - // // - // // Padding( - // // padding: const EdgeInsets.symmetric( - // // vertical: 8.0, - // // ), - // // child: GestureDetector( - // // // onTap: filterDateRange, - // // onTap: () { - // // if (_formKey.currentState!.validate()) { - // // filterDateRange(); // only runs if valid - // // } - // // }, - // // child: Icon(Icons.filter_alt_outlined), - // // ), - // // ), - // // - // // Padding( - // // padding: const EdgeInsets.all(8.0), - // // child: GestureDetector( - // // onTap: refrshfilterDateRange, - // // child: Icon(Icons.refresh), - // // ), - // // ), - // // ], - // // ), - // // ), - // Spacer(), - // ], + Text( + "Invoice", + style: GoogleFonts.poppins( + fontSize: 14, + fontWeight: FontWeight.w400, + ), + ), + + Spacer(), ThemedSearchField( hintText: 'Search', // backgroundColor: Color(0xFFF6F8F8), @@ -383,24 +338,13 @@ class _InvoiceListState extends ConsumerState { ), SizedBox(width: 10), Tooltip( - message: 'Export File', + message: 'Raise Invoice', child: InkWell( onTap: () async { context.go(AppRoutes.payout); }, - // onTap: () { - // print('TAV ExcelExporter'); - // - // ExcelExporter.exportToExcel( - // sheetName: sheetName, - // data: data, - // headers: headers, - // fileName: fileName, - // keys: keys!, - // ); - // }, child: Container( - padding: const EdgeInsets.all(8.0), + padding: const EdgeInsets.all(4.0), decoration: BoxDecoration( color: const Color(0xFF2E7D6E), // color: const Color(0xFF425B5B), @@ -414,7 +358,7 @@ class _InvoiceListState extends ConsumerState { // height: 25, // width: 25, // ), - Icon(Icons.add, color: Colors.white, size: 25), + Icon(Icons.add, color: Colors.white, size: 18), // Image.asset("assets/miscellaneous/export", height: 15, width: 15), ], ), @@ -428,20 +372,22 @@ class _InvoiceListState extends ConsumerState { data: filteredData, txt: true, displayHeaders: [ - "Updated Date", - "Invoice Date", "Invoice No", + "Invoice Date", "Invoice Amount", - "Broker Name", - "Partner Name", + "Broker ", + "POS ", + "Status ", + "Updated Date", ], keys: [ - "updated_at", "invoice_date", "invoice_no", - "invoice_amount", + "invoice_amount_indian_format", "broker_name", - "agent_name", + "pos_name", + "payout_status", + "updated_at", ], ), ], @@ -450,34 +396,32 @@ class _InvoiceListState extends ConsumerState { SizedBox(height: 5), Container( decoration: BoxDecoration( - color: Color(0xFFEDF6F5), + color: Color(0xFFF1F5F9), borderRadius: BorderRadius.circular(6), ), - padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 16), + padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 16), child: Row( children: [ Expanded(flex: 3, child: Text('Invoice No', style: _headerStyle)), Expanded( - flex: 3, + flex: 2, child: Text('Invoice Date', style: _headerStyle), ), Expanded( - flex: 3, + flex: 2, child: Text('Invoice Amount', style: _headerStyle), ), Expanded( - flex: 3, + flex: 2, child: Text('Broker Name', style: _headerStyle), ), + Expanded(flex: 2, child: Text('POS Name', style: _headerStyle)), + Expanded(flex: 2, child: Text('Status', style: _headerStyle)), Expanded( - flex: 3, - child: Text('Partner Name', style: _headerStyle), - ), - Expanded( - flex: 3, + flex: 2, child: Text('Updated Date', style: _headerStyle), ), - Expanded(flex: 3, child: Text('Action', style: _headerStyle)), + Expanded(flex: 2, child: Text('Action', style: _headerStyle)), ], ), ), @@ -516,7 +460,7 @@ class _InvoiceListState extends ConsumerState { Widget _buildDataRow(Map item, sno) { return Container( - padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 16), + padding: const EdgeInsets.symmetric(vertical: 5, horizontal: 16), // margin: const EdgeInsets.only(top: 10), decoration: BoxDecoration( color: Colors.white, @@ -533,40 +477,62 @@ class _InvoiceListState extends ConsumerState { child: Text(item['invoice_no'] ?? '-', style: _dataBold), ), Expanded( - flex: 3, - child: Text(item['invoice_date'] ?? '-', style: _dataBold), + flex: 2, + child: Text( + item['invoice_date'] != null + ? _formatDate(item['invoice_date']) + : '-', + style: _dataBold, + ), ), Expanded( - flex: 3, + flex: 2, child: Text( - item['invoice_amount'] ?? '-', + item['invoice_amount_indian_format'] ?? '-', style: _dataBold, softWrap: true, maxLines: 3, ), ), Expanded( - flex: 3, + flex: 2, child: Text( - item['broker_name'] ?? '-', + item['broker_name'] != null ? item['broker_name'] : '-', style: _dataBold, softWrap: true, maxLines: 3, ), ), Expanded( - flex: 3, + flex: 2, child: Text( - item['agent_name'] ?? '-', + item['pos_name'] != null ? item['pos_name'] : '-', style: _dataBold, softWrap: true, maxLines: 3, ), ), Expanded( - flex: 3, + flex: 2, child: Text( - _formatDate(item['updated_at']) ?? '-', + // item['payout_status'] != null ? item['payout_status'] : '-', + item['payout_status'] == '1' + ? 'Pending' + : item['payout_status'] != null + ? 'Completed' + : '-', + style: _dataBold, + softWrap: true, + maxLines: 3, + ), + ), + + Expanded( + flex: 2, + child: Text( + item['updated_at'] != null + ? _formatDateTime(item['updated_at']) + : '-', style: _dataBold, softWrap: true, maxLines: 3, @@ -577,27 +543,38 @@ class _InvoiceListState extends ConsumerState { Expanded( flex: 2, child: Row( - mainAxisAlignment: MainAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.start, children: [ // EDIT ICON IconButton( - icon: const Icon(Icons.edit, color: Colors.blue, size: 20), + icon: const Icon( + Icons.remove_red_eye_outlined, + color: Colors.blue, + size: 18, + ), onPressed: () { // TODO: handle edit context.go(AppRoutes.payout, extra: item); print("Edit clicked for ${item['invoice_no']}"); }, + splashRadius: 18, + hoverColor: Colors.black12, + padding: const EdgeInsets.all(8), + constraints: const BoxConstraints(), ), - const SizedBox(width: 6), // DELETE ICON IconButton( - icon: const Icon(Icons.delete, color: Colors.red, size: 20), + icon: const Icon(Icons.delete, color: Colors.red, size: 18), onPressed: () { // TODO: handle delete deleteInvoice(item['id']); print("Delete clicked for ${item['invoice_no']}"); }, + splashRadius: 18, + hoverColor: Colors.black12, + padding: const EdgeInsets.all(8), + constraints: const BoxConstraints(), ), ], ), @@ -608,14 +585,14 @@ class _InvoiceListState extends ConsumerState { } static final _dataBold = GoogleFonts.inter( - fontSize: 14, - + fontSize: 11.5, fontWeight: FontWeight.w400, color: Color(0xFF000000), ); - static final _headerStyle = GoogleFonts.inter( - color: Colors.black, - fontWeight: FontWeight.bold, + static final _headerStyle = GoogleFonts.poppins( + fontSize: 11.2, + fontWeight: FontWeight.w500, + color: Color(0xFF1E293B), ); } diff --git a/lib/presentation/screens/payout/payout_old.dart b/lib/presentation/screens/payout/payout_old.dart new file mode 100644 index 0000000..3fad069 --- /dev/null +++ b/lib/presentation/screens/payout/payout_old.dart @@ -0,0 +1,1070 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:go_router/go_router.dart'; +import 'package:intl/intl.dart'; +import 'package:nhance_partner/data/utils/toastNotification.dart'; + +import '../../../core/routing/routes.dart'; +import '../../../core/services/api_service.dart'; +import '../../layouts/main_layout.dart'; +import '../../providers/manager_provider.dart'; +import 'FormFieldBox.dart'; + +class PayOutScreen extends ConsumerStatefulWidget { + final Map? editItem; + + const PayOutScreen({super.key, this.editItem}); + + @override + ConsumerState createState() => _PayOutScreenState(); +} + +class _PayOutScreenState extends ConsumerState { + // -------------------------- + // Invoice Fields + // -------------------------- + late TextEditingController invoiceNoController; + DateTime invoiceDate = DateTime.now(); + int? selectedAgentId; + int? selectedBrokerID; + DateTime? policyTillDate = DateTime.now(); + + // -------------------------- + // Filters + // -------------------------- + DateTime? fromDate; + DateTime? toDate; + + // -------------------------- + // Collapse States + // -------------------------- + bool invoiceCollapsed = false; + bool policyCollapsed = false; + + // -------------------------- + // Sample Data (replace with API later) + // -------------------------- + final List> allPolicies = []; + + List> filteredPolicies = []; + Set selectedPolicies = {}; + + List> getBrokerData = []; + List> filteredBrokerData = []; + + List> getAgentData = []; + List> filteredAgentData = []; + + bool isLoading = false; + late ApiService apiService; + dynamic managerId; + dynamic userId; + final DateFormat df = DateFormat('dd-MM-yyyy'); + + @override + void initState() { + super.initState(); + apiService = ApiService(); + invoiceNoController = TextEditingController(text: generateInvoiceNumber()); + Future.microtask(() async { + managerId = ref.watch(managerIdProvider); + userId = ref.watch(userIdProvider); + if (managerId != null) { + await getAgentList(managerId); + } + await getBroker(); + }); + + // 👉 CHECK EDIT MODE + if (widget.editItem != null) { + print('editItemID ${widget.editItem}'); + final invoiceID = widget.editItem!['id']; + _loadEditData(invoiceID); + } + } + + void _loadEditData(String invoiceID) async { + setState(() => isLoading = true); + + final response = await apiService.getInvoiceDetails(invoiceID); + + if (response['status'] == 'success') { + final invoice = response['data']['invoice']; + final items = List>.from(response['data']['items']); + + print("Invoice → $invoice"); + print("Items → $items"); + + // ----------------------------- + // 1. SET BASIC FIELDS + // ----------------------------- + setState(() { + selectedBrokerID = int.tryParse(invoice['broker_id'].toString()); + selectedAgentId = int.tryParse(invoice['agent_id'].toString()); + + invoiceNoController.text = invoice['invoice_no']; + invoiceDate = DateTime.parse(invoice['invoice_date']); + + if (invoice['till_date'] != "0000-00-00") { + policyTillDate = DateTime.parse(invoice['till_date']); + } else { + policyTillDate = DateTime.now(); + } + }); + print('selectedBrokerID $selectedBrokerID'); + + // ----------------------------- + // 3. LOAD ALL POLICIES FOR THIS BROKER + AGENT + // ----------------------------- + await loadPolicies(); + + // ----------------------------- + // 2. SET SELECTED POLICIES + // ----------------------------- + selectedPolicies = items + .map((p) => int.parse(p['policy_id'].toString())) + .toSet(); + + // ----------------------------- + // 3. LOAD ALL POLICIES FOR THIS BROKER + AGENT + // ----------------------------- + await loadPolicies(); + + // ----------------------------- + // 4. KEEP ONLY POLICIES THAT MATCH API DATA + // ----------------------------- + setState(() { + filteredPolicies = filteredPolicies.map((p) { + final policyId = int.parse(p['policy_id']); + return {...p, 'is_selected': selectedPolicies.contains(policyId)}; + }).toList(); + }); + } + + setState(() => isLoading = false); + } + + String formatDate(String date) { + final d = DateTime.parse(date); + return DateFormat('dd-MM-yyyy').format(d); + } + + // Get Broker List + Future getBroker() async { + print('getBroker called'); + setState(() { + isLoading = true; + }); + + try { + final response = await apiService.fetchMasterDropDown('Broker'); + + if (response['status'] == 200) { + print('getBroker - ${response['data']}'); + setState(() { + getBrokerData = List>.from(response['data']); + print('API Data - $getBrokerData'); + + filteredBrokerData = List.from(getBrokerData); + print('originalData - $filteredBrokerData'); + }); + } else { + getBrokerData = []; + filteredBrokerData = []; + } + } catch (e) { + print('Exception occurred: $e'); + } finally { + setState(() { + isLoading = false; + }); + } + } + + //Get Agent List + Future getAgentList(int id) async { + print('getClaimList called'); + setState(() { + isLoading = true; + }); + + try { + final response = await apiService.fetchAgentUserList(managerId); + + if (response['status'] == 'success') { + print('getAgentListData - ${response['data']}'); + setState(() { + getAgentData = List>.from(response['data']); + print('API Data - $getAgentData'); + + filteredAgentData = List.from(getAgentData); + print('originalData - $filteredAgentData'); + }); + } else { + getAgentData = []; + filteredAgentData = []; + } + } catch (e) { + print('Exception occurred: $e'); + } finally { + setState(() { + isLoading = false; + }); + } + } + + // Generate invoice number + String generateInvoiceNumber() { + final now = DateTime.now(); + final random = now.millisecondsSinceEpoch % 1000; + return "INV${now.year}${now.month.toString().padLeft(2, '0')}${random.toString().padLeft(3, '0')}"; + } + + // Pick date helper + Future pickDate(DateTime initial) async { + return await showDatePicker( + context: context, + initialDate: initial, + firstDate: DateTime(2000), + lastDate: DateTime(2100), + ); + } + + // Load policies (simulate API) + Future loadPolicies() async { + try { + String formattedTillDate = DateFormat( + 'yyyy-MM-dd', + ).format(policyTillDate!); + final jsondata = { + "broker_id": selectedBrokerID, + "agent_id": selectedAgentId, + "issued_date": formattedTillDate, + }; + final response = await apiService.getCommissionRateList(jsondata); + + if (response['status'] == 'success') { + print('API Data response - $response'); + setState(() { + filteredPolicies = List>.from(response['data']); + print('API Data - $filteredPolicies'); + // filteredPolicies = allPolicies.where((p) { + // if (p["agentId"] != selectedAgentId) return false; + // if (policyTillDate != null && + // DateTime.parse(p["date"]).isAfter(policyTillDate!)) + // return false; + // return true; + // }).toList(); + }); + } else { + filteredPolicies = []; + } + } catch (e) { + print('Exception occurred: $e'); + } finally { + setState(() { + isLoading = false; + }); + } + } + + // Apply date filters + void filterPolicies() { + if (selectedAgentId == null) return; + + setState(() { + filteredPolicies = allPolicies.where((p) { + final date = DateTime.parse(p["date"]); + // if (p["agentId"] != selectedAgentId) return false; + // if (policyTillDate != null && date.isAfter(policyTillDate!)) + // return false; + if (fromDate != null && date.isBefore(fromDate!)) return false; + if (toDate != null && date.isAfter(toDate!)) return false; + return true; + }).toList(); + }); + } + + // Save invoice (simulation) + Future saveInvoice() async { + try { + if (selectedAgentId == null) { + ToastHelper.showWarningToast(context, "Please select an agent"); + return; + } + + if (selectedPolicies.isEmpty) { + ToastHelper.showWarningToast( + context, + "Please select at least 1 policy", + ); + return; + } + + // Format dates for API + final String formattedInvoiceDate = DateFormat( + 'yyyy-MM-dd', + ).format(invoiceDate); + final String formattedTillDate = DateFormat( + 'yyyy-MM-dd', + ).format(policyTillDate!); + + // Total Commission + final double totalCommission = filteredPolicies + .where((p) => selectedPolicies.contains(int.parse(p["policy_id"]))) + .fold(0.0, (sum, p) => sum + double.parse(p["commission_amount"])); + + // Build items array + final List> items = filteredPolicies + .where((p) => selectedPolicies.contains(int.parse(p["policy_id"]))) + .map( + (p) => { + "policy_id": int.parse(p["policy_id"]), + "policy_no": p["policy_no"], + "commission_amount": double.parse(p["commission_amount"]), + }, + ) + .toList(); + + final jsondata = { + "invoice_no": invoiceNoController.text, + "invoice_amount": totalCommission, + "agent_id": selectedAgentId, + "broker_id": selectedBrokerID, + "invoice_date": formattedInvoiceDate, + "till_date": formattedTillDate, + "payout_status": 0, + "created_by": userId, + "updated_by": userId, + "items": items, + }; + print('Commissions $jsondata'); + final response = await apiService.getCreateOrUpdate(jsondata); + + if (response['status'] == 'success') { + setState(() { + print('API Data response - $response'); + selectedPolicies.clear(); + invoiceNoController.clear(); + invoiceDate = DateTime.now(); + policyTillDate = DateTime.now(); + filteredPolicies = []; + }); + context.go(AppRoutes.invoiceList); + } else { + filteredPolicies = []; + } + } catch (e) { + print('Exception occurred: $e'); + } finally { + setState(() { + isLoading = false; + }); + } + + // showDialog( + // context: context, + // builder: (_) => AlertDialog( + // title: const Text("Success"), + // content: Text( + // "Invoice No: ${invoiceNoController.text}\n" + // "Total Commission: ₹$totalComm", + // ), + // actions: [ + // TextButton( + // onPressed: () => Navigator.pop(context), + // child: const Text("OK"), + // ), + // ], + // ), + // ); + } + + void showMessage(String msg) { + ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(msg))); + } + + @override + Widget build(BuildContext context) { + final df = DateFormat("dd-MM-yyyy"); + + return MainLayout( + title: "Pay Out", + body: Stack( + children: [ + SingleChildScrollView( + padding: const EdgeInsets.all(16), + child: Column( + children: [ + // ---------------------- + // Invoice Details + // ---------------------- + Card( + color: Colors.white, // White background + clipBehavior: Clip.none, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), + ), + elevation: 1, + child: Column( + children: [ + // ---------- HEADER (CLICK TO COLLAPSE) ---------- + ListTile( + title: const Text("Invoice Details"), + trailing: Icon( + invoiceCollapsed + ? Icons.expand_more + : Icons.expand_less, + color: Color(0xFF2F766E), + size: 26, + ), + onTap: () => setState( + () => invoiceCollapsed = !invoiceCollapsed, + ), + ), + // ------------------- HR LINE WHEN OPEN ------------------- + if (!invoiceCollapsed) + const Divider( + height: 1, + thickness: 1, + color: Color(0xFFE0E0E0), + ), + + // ---------- COLLAPSIBLE CONTENT ---------- + AnimatedCrossFade( + duration: const Duration(milliseconds: 500), + crossFadeState: invoiceCollapsed + ? CrossFadeState.showFirst + : CrossFadeState.showSecond, + firstChild: const SizedBox( + // FIXED + height: 0, + width: double.infinity, + ), + secondChild: Padding( + padding: const EdgeInsets.all(16), + child: Column( + children: [ + // ------------------- ROW OF FIELDS ------------------- + SizedBox( + height: 80, // 👈 ensures bottom alignment works + child: Row( + crossAxisAlignment: CrossAxisAlignment + .center, // 👈 bottom align everything + children: [ + // INVOICE NO + FormFieldBox( + label: "Invoice No *", + child: TextField( + controller: invoiceNoController, + readOnly: true, + decoration: commonInputDecoration( + hint: "Invoice Number", + ), + ), + ), + const SizedBox(width: 12), + + // INVOICE DATE + FormFieldBox( + label: "Invoice Date *", + child: GestureDetector( + onTap: () async { + final picked = await pickDate( + invoiceDate, + ); + if (picked != null) + setState( + () => invoiceDate = picked, + ); + }, + child: AbsorbPointer( + child: TextField( + readOnly: true, + decoration: + commonInputDecoration( + hint: df.format(invoiceDate), + ).copyWith( + suffixIcon: const Icon( + Icons.calendar_today, + size: 18, + ), + ), + ), + ), + ), + ), + const SizedBox(width: 12), + + // BROKER + FormFieldBox( + label: "Select Broker *", + child: DropdownButtonFormField( + value: selectedBrokerID, + decoration: commonInputDecoration( + hint: "Select Broker", + ), + items: filteredBrokerData.map((a) { + final int id = + int.tryParse( + a['id'].toString(), + ) ?? + 0; + return DropdownMenuItem( + value: id, + child: Text(a['name'].toString()), + ); + }).toList(), + onChanged: (id) => setState( + () => selectedBrokerID = id, + ), + ), + ), + const SizedBox(width: 12), + + // AGENT + FormFieldBox( + label: "Select Agent *", + child: DropdownButtonFormField( + value: selectedAgentId, + decoration: commonInputDecoration( + hint: "Select Agent", + ), + items: filteredAgentData.map((a) { + final int id = + int.tryParse( + a['id'].toString(), + ) ?? + 0; + return DropdownMenuItem( + value: id, + child: Text(a['name'].toString()), + ); + }).toList(), + onChanged: (id) => setState( + () => selectedAgentId = id, + ), + ), + ), + const SizedBox(width: 12), + + // POLICY TILL DATE + FormFieldBox( + label: "Policy Till Date *", + child: GestureDetector( + onTap: () async { + final picked = await pickDate( + policyTillDate!, + ); + if (picked != null) + setState( + () => policyTillDate = picked, + ); + }, + child: AbsorbPointer( + child: TextField( + readOnly: true, + decoration: + commonInputDecoration( + hint: df.format( + policyTillDate!, + ), + ).copyWith( + suffixIcon: const Icon( + Icons.calendar_today, + size: 18, + ), + ), + ), + ), + ), + ), + const SizedBox(width: 12), + + // --- RESET + SEARCH (BOTTOM ALIGNED) --- + Column( + mainAxisAlignment: + MainAxisAlignment.center, + children: [ + Row( + children: [ + // RESET + SizedBox( + height: 30, + width: 30, + child: OutlinedButton( + style: OutlinedButton.styleFrom( + padding: EdgeInsets.zero, + ), + onPressed: () { + setState(() { + selectedBrokerID = null; + selectedAgentId = null; + fromDate = null; + toDate = null; + policyTillDate = + DateTime.now(); + filteredPolicies = []; + selectedPolicies.clear(); + }); + }, + child: const Icon( + Icons.refresh, + size: 18, + ), + ), + ), + const SizedBox(width: 12), + + // SEARCH + SizedBox( + height: 30, + width: 30, + child: OutlinedButton( + style: OutlinedButton.styleFrom( + padding: EdgeInsets.zero, + ), + onPressed: loadPolicies, + child: const Icon( + Icons.search, + size: 18, + ), + ), + ), + ], + ), + ], + ), + ], + ), + ), + ], + ), + ), + ), + ], + ), + ), + + const SizedBox(height: 20), + + // ---------------------- + // Policy Selection + // ---------------------- + Card( + color: Colors.white, // White background + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), + ), + elevation: 1, + child: Column( + children: [ + ListTile( + // contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 4), + title: const Text("Policy Selection"), + // ---------------- RIGHT SIDE (filters + collapse) ---------------- + trailing: Row( + mainAxisSize: MainAxisSize.min, + children: [ + // FROM DATE + SizedBox( + height: 30, + width: MediaQuery.of(context).size.width * 0.12, + child: GestureDetector( + onTap: () async { + final date = await pickDate( + fromDate ?? DateTime.now(), + ); + if (date != null) + setState(() => fromDate = date); + }, + child: AbsorbPointer( + child: TextField( + readOnly: true, + decoration: + commonInputDecoration( + hint: "From Date", + ).copyWith( + // 👇 THIS is where you set formatted date + suffixIcon: const Icon( + Icons.calendar_today, + size: 18, + ), + labelText: fromDate == null + ? "From Date" + : df.format(fromDate!), + ), + ), + ), + ), + ), + const SizedBox(width: 8), + + // TO DATE + SizedBox( + height: 30, + width: MediaQuery.of(context).size.width * 0.12, + child: GestureDetector( + onTap: () async { + final date = await pickDate( + toDate ?? DateTime.now(), + ); + if (date != null) + setState(() => toDate = date); + }, + child: AbsorbPointer( + child: TextField( + readOnly: true, + decoration: + commonInputDecoration( + hint: "To Date", + ).copyWith( + // 👇 THIS is where you set formatted date + suffixIcon: const Icon( + Icons.calendar_today, + size: 18, + ), + labelText: toDate == null + ? "To Date" + : df.format(toDate!), + ), + ), + ), + ), + ), + const SizedBox(width: 8), + + // RESET BUTTON + SizedBox( + height: 30, + width: 30, + child: OutlinedButton( + style: OutlinedButton.styleFrom( + padding: EdgeInsets.zero, + ), + onPressed: () { + setState(() { + fromDate = null; + toDate = null; + }); + loadPolicies(); + }, + child: const Icon(Icons.refresh, size: 18), + ), + ), + const SizedBox(width: 8), + + // SEARCH BUTTON + SizedBox( + height: 30, + width: 30, + child: OutlinedButton( + style: OutlinedButton.styleFrom( + padding: EdgeInsets.zero, + ), + onPressed: filterPolicies, + child: const Icon(Icons.search, size: 18), + ), + ), + const SizedBox(width: 8), + + // COLLAPSE ICON + GestureDetector( + onTap: () => setState( + () => policyCollapsed = !policyCollapsed, + ), + child: Icon( + policyCollapsed + ? Icons.expand_more + : Icons.expand_less, + color: Colors.teal, + size: 26, + ), + ), + ], + ), + onTap: () => + setState(() => policyCollapsed = !policyCollapsed), + ), + // ------------------- HR LINE WHEN OPEN ------------------- + if (!policyCollapsed) + const Divider( + height: 1, + thickness: 1, + color: Color(0xFFE0E0E0), + ), + + AnimatedCrossFade( + duration: const Duration(milliseconds: 500), + crossFadeState: policyCollapsed + ? CrossFadeState.showFirst + : CrossFadeState.showSecond, + firstChild: Container(), + secondChild: Padding( + padding: const EdgeInsets.all(16), + child: Column( + children: [ + // ---------------- SELECT ALL ---------------- + if (filteredPolicies.isNotEmpty) + Container( + width: double.infinity, + padding: const EdgeInsets.symmetric( + vertical: 8, + horizontal: 16, + ), + child: Row( + children: [ + Checkbox( + value: + filteredPolicies.isNotEmpty && + filteredPolicies.every( + (p) => selectedPolicies.contains( + int.parse(p["policy_id"]), + ), + ), + onChanged: (v) { + setState(() { + if (v == true) { + selectedPolicies = + filteredPolicies + .map( + (p) => int.parse( + p["policy_id"], + ), + ) + .toSet(); + } else { + selectedPolicies.clear(); + } + }); + }, + ), + const SizedBox(width: 8), + const Text( + "Select All Policies", + style: TextStyle(fontSize: 16), + ), + ], + ), + ), + + // ---------------- POLICY LIST ---------------- + if (filteredPolicies.isEmpty) + const Padding( + padding: EdgeInsets.all(20), + child: Text("No policies found"), + ) + else + ...filteredPolicies.map((p) { + // final int id = int.parse(p["policy_id"]); + // final bool isSelected = selectedPolicies.contains(id); + + // ---- SAFE ID PARSING ---- + final String policyIdStr = + p["policy_id"]?.toString() ?? "0"; + final int id = int.tryParse(policyIdStr) ?? 0; + + final bool isSelected = selectedPolicies + .contains(id); + + // ---- SAFE FIELDS ---- + final String policyNo = + p["policy_no"]?.toString() ?? "-"; + final String customer = + p["customer_name"]?.toString() ?? "-"; + + final String premiumStr = + p["premium_amount"]?.toString() ?? "0"; + final double premium = + double.tryParse(premiumStr) ?? 0; + + final String commissionStr = + p["commission_amount"]?.toString() ?? "0"; + final double commission = + double.tryParse(commissionStr) ?? 0; + + return ListTile( + tileColor: isSelected + ? Colors.teal.shade50 + : null, + leading: Checkbox( + value: isSelected, + onChanged: (v) { + setState(() { + if (v == true) { + selectedPolicies.add(id); + } else { + selectedPolicies.remove(id); + } + }); + }, + ), + + title: Text(policyNo), + + subtitle: Text( + "Customer: ${customer} | " + "Premium: ₹${premium}", + ), + + trailing: Text( + "₹${commission}", + style: const TextStyle( + color: Colors.teal, + fontWeight: FontWeight.bold, + ), + ), + + onTap: () { + setState(() { + if (isSelected) { + selectedPolicies.remove(id); + } else { + selectedPolicies.add(id); + } + }); + }, + ); + }).toList(), + ], + ), + ), + ), + ], + ), + ), + + const SizedBox(height: 100), + ], + ), + ), + + // ------------------------------ + // Bottom Summary Bar + // ------------------------------ + if (selectedPolicies.isNotEmpty) + Positioned( + left: 0, + right: 0, + bottom: 0, + child: Material( + elevation: 8, + child: Container( + padding: const EdgeInsets.all(16), + color: Colors.white, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + // ---------------- SELECTED COUNT ---------------- + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text( + "Selected Policies", + style: TextStyle(fontSize: 12), + ), + const SizedBox(height: 4), + Text( + selectedPolicies.length.toString(), + style: const TextStyle( + fontSize: 18, + fontWeight: FontWeight.bold, + ), + ), + ], + ), + + // ---------------- TOTAL COMMISSION ---------------- + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text( + "Total Commission", + style: TextStyle(fontSize: 12), + ), + const SizedBox(height: 4), + + /// NULL-SAFE TOTAL COMMISSION CALCULATION + Text( + "₹${_calculateTotalCommission()}", + style: const TextStyle( + fontSize: 18, + fontWeight: FontWeight.bold, + color: Colors.teal, + ), + ), + ], + ), + + // ---------------- BUTTONS ---------------- + Row( + children: [ + TextButton( + onPressed: () => + setState(() => selectedPolicies.clear()), + child: const Text("Clear"), + ), + const SizedBox(width: 10), + ElevatedButton( + onPressed: saveInvoice, + style: ElevatedButton.styleFrom( + backgroundColor: Colors.orange, + ), + child: const Text("Submit"), + ), + ], + ), + ], + ), + ), + ), + ), + ], + ), + ); + } + + double _calculateTotalCommission() { + double total = 0.0; + + for (var p in filteredPolicies) { + // Safe policy_id parsing + final String idStr = p["policy_id"]?.toString() ?? "0"; + final int id = int.tryParse(idStr) ?? 0; + + if (!selectedPolicies.contains(id)) continue; + + // Safe commission amount + final String commStr = p["commission_amount"]?.toString() ?? "0"; + final double comm = double.tryParse(commStr) ?? 0; + + total += comm; + } + + // Remove decimal points (.00) + return total.roundToDouble(); + } + + InputDecoration commonInputDecoration({required String hint}) { + return InputDecoration( + hintText: hint, + filled: true, + fillColor: Colors.white, + contentPadding: const EdgeInsets.symmetric(horizontal: 12), + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(6), + borderSide: const BorderSide( + color: Color(0xFFDDDDDD), // light gray + width: 1, + ), + ), + focusedBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(6), + borderSide: const BorderSide( + color: Color(0xFFAAAAAA), // darker gray on focus + width: 1.2, + ), + ), + ); + } +} diff --git a/lib/presentation/screens/payout/payout_screen.dart b/lib/presentation/screens/payout/payout_screen.dart index 3fad069..f46bc16 100644 --- a/lib/presentation/screens/payout/payout_screen.dart +++ b/lib/presentation/screens/payout/payout_screen.dart @@ -1,14 +1,22 @@ +import 'dart:convert'; + +import 'package:dropdown_search/dropdown_search.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:go_router/go_router.dart'; +import 'package:google_fonts/google_fonts.dart'; import 'package:intl/intl.dart'; import 'package:nhance_partner/data/utils/toastNotification.dart'; import '../../../core/routing/routes.dart'; import '../../../core/services/api_service.dart'; import '../../layouts/main_layout.dart'; +import '../../layouts/responsive_layout.dart'; import '../../providers/manager_provider.dart'; +import '../../providers/userRoleProvider.dart'; +import '../../themes/indicators/input_field_decoration.dart'; import 'FormFieldBox.dart'; +import 'custom_dateRange.dart'; class PayOutScreen extends ConsumerStatefulWidget { final Map? editItem; @@ -23,18 +31,37 @@ class _PayOutScreenState extends ConsumerState { // -------------------------- // Invoice Fields // -------------------------- - late TextEditingController invoiceNoController; + final _formKey = GlobalKey(); + // final TextEditingController invoiceNoController; DateTime invoiceDate = DateTime.now(); - int? selectedAgentId; - int? selectedBrokerID; + // dynamic selectedAgentId; + List? selectedAgentId; + dynamic selectedBrokerID; + dynamic selectedPOS; DateTime? policyTillDate = DateTime.now(); + dynamic totalPolicies = '0'; + dynamic totalCommission = '0'; + bool isEdit = false; + + final GlobalKey>> dropDownKeyPOS = + GlobalKey>>(); + // -------------------------- // Filters // -------------------------- DateTime? fromDate; DateTime? toDate; + Map controllers = {}; + List tabHeader = [ + 'startDate', + 'endDate', + 'invoiceNo', + 'invoiceDate', + 'invoiceStatus', + ]; + // -------------------------- // Collapse States // -------------------------- @@ -55,22 +82,34 @@ class _PayOutScreenState extends ConsumerState { List> getAgentData = []; List> filteredAgentData = []; + List> getPOSDataList = []; + List> originalPOSData = []; + List> filteredPOSData = []; + bool isLoading = false; + bool isLoadingPOS = false; late ApiService apiService; dynamic managerId; dynamic userId; + dynamic roleId; final DateFormat df = DateFormat('dd-MM-yyyy'); @override void initState() { super.initState(); apiService = ApiService(); - invoiceNoController = TextEditingController(text: generateInvoiceNumber()); + + for (String field in tabHeader) { + controllers[field] = TextEditingController(); + } + // invoiceNoController = TextEditingController(text: generateInvoiceNumber()); Future.microtask(() async { managerId = ref.watch(managerIdProvider); userId = ref.watch(userIdProvider); + roleId = ref.read(userRoleProvider); if (managerId != null) { - await getAgentList(managerId); + getAgentList(managerId); + getPosList(managerId); } await getBroker(); }); @@ -78,67 +117,193 @@ class _PayOutScreenState extends ConsumerState { // 👉 CHECK EDIT MODE if (widget.editItem != null) { print('editItemID ${widget.editItem}'); + final invoiceID = widget.editItem!['id']; - _loadEditData(invoiceID); + final brokerID = widget.editItem!['broker_id']; + + // Invoice No + controllers['invoiceNo']?.text = widget.editItem!['invoice_no']; + + // Invoice Date (UI format) + controllers['invoiceDate']?.text = + widget.editItem!['invoice_date_ui_format']; + + controllers['invoiceStatus']?.text = widget.editItem!['payout_status']; + + print('invoiceID $invoiceID'); + print('brokerID $brokerID'); + _loadEditData(invoiceID, brokerID); } } - void _loadEditData(String invoiceID) async { - setState(() => isLoading = true); + Future getPosList(int id) async { + print('E104 => Fns called => $id'); - final response = await apiService.getInvoiceDetails(invoiceID); + final val = 'dropDown'; + setState(() { + isLoadingPOS = true; + }); + + try { + final response = await apiService.fetchPosList(id, val); + + if (response['status'] == 200) { + print('E113 => getStaffListData => ${response['data']}'); + setState(() { + getPOSDataList = List>.from(response['data']); + originalPOSData = getPOSDataList; + filteredPOSData = List.from(originalPOSData); + // print('originalData - $getClaimPolicies'); + }); + } else { + getPOSDataList = []; + originalPOSData = []; + } + } catch (e) { + print('Exception occurred: $e'); + } finally { + setState(() { + isLoadingPOS = false; + }); + } + } + + void refresh() { + setState(() { + for (var controller in controllers.values) { + controller.clear(); + controller.dispose(); + } + + selectedPOS = ''; + selectedBrokerID = ''; + selectedAgentId = []; + selectedPolicies = {}; + totalPolicies = ''; + totalCommission = ''; + }); + } + + void _loadEditData(String invoiceID, brokerID) async { + setState(() => isLoading = true); + print('_loadEditData'); + + final jsondata = { + // "id": invoiceID, + "invoice_id": invoiceID, + "broker_id": int.tryParse(brokerID ?? ''), + + // "issued_date": formattedTillDate, + }; + print('_loadEditData - $jsondata'); + final response = await apiService.getCommissionRateList(jsondata); + // final response = await apiService.getInvoiceDetails(invoiceID); if (response['status'] == 'success') { - final invoice = response['data']['invoice']; - final items = List>.from(response['data']['items']); - + print('_loadEditData success'); + print('_loadEditData success - ${response['data']}'); + final invoice = response['data']; print("Invoice → $invoice"); + // final items = List>.from(response['data']['items']); + final items = response['data']; + final totPolicy = response['total_policies'].toString() ?? "0"; + final totalCommiss = response['total_commission']; + + setState(() { + // selectedPolicies = response['total_policies']; + isEdit = true; + totalPolicies = totPolicy; + totalCommission = totalCommiss; + + // filteredPolicies = items.map((p) { + // final policyId = int.parse(p['policy_id']); + // return {...p, 'is_selected': selectedPolicies.contains(policyId)}; + // }).toList(); + + final List> items = (response['data'] as List) + .map((e) => Map.from(e)) + .toList(); + + filteredPolicies = List>.from(response['data']); + + print("selectedPolicies → $filteredPolicies "); + }); + print( + "selectedPolicies → $selectedPolicies ->${response['total_policies']}", + ); print("Items → $items"); // ----------------------------- // 1. SET BASIC FIELDS // ----------------------------- - setState(() { - selectedBrokerID = int.tryParse(invoice['broker_id'].toString()); - selectedAgentId = int.tryParse(invoice['agent_id'].toString()); - - invoiceNoController.text = invoice['invoice_no']; - invoiceDate = DateTime.parse(invoice['invoice_date']); - - if (invoice['till_date'] != "0000-00-00") { - policyTillDate = DateTime.parse(invoice['till_date']); - } else { - policyTillDate = DateTime.now(); - } - }); - print('selectedBrokerID $selectedBrokerID'); + // setState(() { + // // selectedAgentId = int.tryParse(invoice['agent_id'].toString()); + // + // // invoiceNoController.text = invoice['invoice_no']; + // // invoiceDate = DateTime.parse(invoice['invoice_date']); + // + // controllers['startDate']?.text = invoice['from_date']; + // controllers['endDate']?.text = invoice['to_date']; + // selectedBrokerID = int.tryParse(invoice['broker_id'].toString()); + // if (invoice['agent_id'] is List) { + // selectedAgentId = (invoice['agent_id'] as List) + // .map((e) => e.toString()) + // .toList(); + // } else if (invoice['agent_id'] is String) { + // final raw = invoice['agent_id'].toString(); + // + // if (raw.startsWith('[')) { + // // JSON string: "[1,2]" + // selectedAgentId = List.from( + // jsonDecode(raw).map((e) => e.toString()), + // ); + // } else if (raw.contains(',')) { + // // Comma separated: "1,2" + // selectedAgentId = raw.split(',').map((e) => e.trim()).toList(); + // } else if (raw.isNotEmpty) { + // // Single value: "1" + // selectedAgentId = [raw]; + // } else { + // selectedAgentId = []; + // } + // } else { + // selectedAgentId = []; + // } + // + // // if (invoice['till_date'] != "0000-00-00") { + // // policyTillDate = DateTime.parse(invoice['till_date']); + // // } else { + // // policyTillDate = DateTime.now(); + // // } + // }); + // print('selectedBrokerID $selectedBrokerID'); // ----------------------------- // 3. LOAD ALL POLICIES FOR THIS BROKER + AGENT // ----------------------------- - await loadPolicies(); + // await loadPolicies(); // ----------------------------- // 2. SET SELECTED POLICIES // ----------------------------- - selectedPolicies = items - .map((p) => int.parse(p['policy_id'].toString())) - .toSet(); + // selectedPolicies = items + // .map((p) => int.parse(p['policy_id'].toString())) + // .toSet(); // ----------------------------- // 3. LOAD ALL POLICIES FOR THIS BROKER + AGENT // ----------------------------- - await loadPolicies(); + // await loadPolicies(); // ----------------------------- // 4. KEEP ONLY POLICIES THAT MATCH API DATA // ----------------------------- - setState(() { - filteredPolicies = filteredPolicies.map((p) { - final policyId = int.parse(p['policy_id']); - return {...p, 'is_selected': selectedPolicies.contains(policyId)}; - }).toList(); - }); + // setState(() { + // filteredPolicies = filteredPolicies.map((p) { + // final policyId = int.parse(p['policy_id']); + // return {...p, 'is_selected': selectedPolicies.contains(policyId)}; + // }).toList(); + // }); } setState(() => isLoading = false); @@ -232,14 +397,18 @@ class _PayOutScreenState extends ConsumerState { // Load policies (simulate API) Future loadPolicies() async { + print('loadPolicies'); try { - String formattedTillDate = DateFormat( - 'yyyy-MM-dd', - ).format(policyTillDate!); + final List agentIds = (selectedAgentId ?? []) + .map((e) => int.parse(e)) + .toList(); + final jsondata = { - "broker_id": selectedBrokerID, - "agent_id": selectedAgentId, - "issued_date": formattedTillDate, + "from_date": controllers['startDate']?.text, + "to_date": controllers['endDate']?.text, + "broker_id": int.tryParse(selectedBrokerID ?? ''), + "agent_id": agentIds, + // "issued_date": formattedTillDate, }; final response = await apiService.getCommissionRateList(jsondata); @@ -293,6 +462,11 @@ class _PayOutScreenState extends ConsumerState { return; } + if (selectedPOS == null) { + ToastHelper.showWarningToast(context, "Please select POS"); + return; + } + if (selectedPolicies.isEmpty) { ToastHelper.showWarningToast( context, @@ -327,25 +501,29 @@ class _PayOutScreenState extends ConsumerState { .toList(); final jsondata = { - "invoice_no": invoiceNoController.text, + "invoice_no": controllers['invoiceNo']?.text, "invoice_amount": totalCommission, "agent_id": selectedAgentId, "broker_id": selectedBrokerID, "invoice_date": formattedInvoiceDate, "till_date": formattedTillDate, "payout_status": 0, + "pos_id": selectedPOS, "created_by": userId, "updated_by": userId, "items": items, }; print('Commissions $jsondata'); + final bool confirm = await showSaveConfirmation(context); + if (!confirm) return; + final response = await apiService.getCreateOrUpdate(jsondata); if (response['status'] == 'success') { setState(() { print('API Data response - $response'); selectedPolicies.clear(); - invoiceNoController.clear(); + controllers['invoiceNo']?.clear(); invoiceDate = DateTime.now(); policyTillDate = DateTime.now(); filteredPolicies = []; @@ -361,669 +539,680 @@ class _PayOutScreenState extends ConsumerState { isLoading = false; }); } - - // showDialog( - // context: context, - // builder: (_) => AlertDialog( - // title: const Text("Success"), - // content: Text( - // "Invoice No: ${invoiceNoController.text}\n" - // "Total Commission: ₹$totalComm", - // ), - // actions: [ - // TextButton( - // onPressed: () => Navigator.pop(context), - // child: const Text("OK"), - // ), - // ], - // ), - // ); } void showMessage(String msg) { ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(msg))); } + Future showSaveConfirmation(BuildContext context) async { + return await showDialog( + context: context, + barrierDismissible: false, + builder: (context) { + return AlertDialog( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), + ), + title: Text( + "Confirm Save", + style: GoogleFonts.inter( + fontSize: 16, + fontWeight: FontWeight.w600, + ), + ), + content: Text( + "Once saved, changes cannot be edited.\nAre you sure you want to continue?", + style: GoogleFonts.inter(fontSize: 13, color: Colors.grey[700]), + ), + actions: [ + TextButton( + onPressed: () => Navigator.pop(context, false), + child: Text( + "No", + style: GoogleFonts.inter( + fontWeight: FontWeight.w500, + color: Colors.grey[700], + ), + ), + ), + ElevatedButton( + style: ElevatedButton.styleFrom( + backgroundColor: const Color(0xFF059669), // emerald + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + ), + ), + onPressed: () => Navigator.pop(context, true), + child: Text( + "Yes, Save", + style: GoogleFonts.inter( + fontWeight: FontWeight.w600, + color: Colors.white, + ), + ), + ), + ], + ); + }, + ) ?? + false; + } + @override Widget build(BuildContext context) { final df = DateFormat("dd-MM-yyyy"); + final int status = + int.tryParse(controllers['invoiceStatus']?.text ?? '0') ?? 0; + + final bool isPending = status == 1; return MainLayout( title: "Pay Out", - body: Stack( - children: [ - SingleChildScrollView( - padding: const EdgeInsets.all(16), - child: Column( - children: [ - // ---------------------- - // Invoice Details - // ---------------------- - Card( - color: Colors.white, // White background - clipBehavior: Clip.none, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(12), - ), - elevation: 1, - child: Column( - children: [ - // ---------- HEADER (CLICK TO COLLAPSE) ---------- - ListTile( - title: const Text("Invoice Details"), - trailing: Icon( - invoiceCollapsed - ? Icons.expand_more - : Icons.expand_less, - color: Color(0xFF2F766E), - size: 26, + body: Container( + color: Colors.white, + padding: const EdgeInsets.all(8), + margin: isEdit + ? EdgeInsets.symmetric( + horizontal: MediaQuery.of(context).size.width * 0.1, + ) + : null, + child: Column( + children: [ + // const SizedBox(height: 20), + Container( + height: 40, + + width: MediaQuery.of(context).size.width, + child: GestureDetector( + onTap: null, + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Tooltip( + message: 'Back', + child: IconButton( + icon: const Icon( + Icons.arrow_left_sharp, + size: 25, + color: Color(0xFF425B5B), ), - onTap: () => setState( - () => invoiceCollapsed = !invoiceCollapsed, + onPressed: () { + context.go(AppRoutes.invoiceList); + }, + splashRadius: 18, + hoverColor: Colors.black12, + padding: const EdgeInsets.all(4), + constraints: const BoxConstraints(), + ), + ), + const SizedBox(width: 5), // spacing between icon and text + Text( + "Invoice", + style: GoogleFonts.inter( + fontSize: 14, + fontWeight: FontWeight.w500, + ), + ), + + // 🟢 Status Chip + if (isEdit) ...[ + Spacer(), + Text( + controllers['invoiceNo']?.text ?? '', + style: GoogleFonts.inter( + fontSize: 13, + fontWeight: FontWeight.w500, + color: const Color(0xFF374151), ), ), - // ------------------- HR LINE WHEN OPEN ------------------- - if (!invoiceCollapsed) - const Divider( - height: 1, - thickness: 1, - color: Color(0xFFE0E0E0), + SizedBox(width: 10), + Text( + // controllers['invoiceDate']?.text ?? '', + '(${controllers['invoiceDate']?.text ?? ''})', + style: GoogleFonts.inter( + fontSize: 11, + fontWeight: FontWeight.w300, + color: const Color(0xFF6B7280), ), - - // ---------- COLLAPSIBLE CONTENT ---------- - AnimatedCrossFade( - duration: const Duration(milliseconds: 500), - crossFadeState: invoiceCollapsed - ? CrossFadeState.showFirst - : CrossFadeState.showSecond, - firstChild: const SizedBox( - // FIXED - height: 0, - width: double.infinity, - ), - secondChild: Padding( - padding: const EdgeInsets.all(16), - child: Column( - children: [ - // ------------------- ROW OF FIELDS ------------------- - SizedBox( - height: 80, // 👈 ensures bottom alignment works - child: Row( - crossAxisAlignment: CrossAxisAlignment - .center, // 👈 bottom align everything - children: [ - // INVOICE NO - FormFieldBox( - label: "Invoice No *", - child: TextField( - controller: invoiceNoController, - readOnly: true, - decoration: commonInputDecoration( - hint: "Invoice Number", - ), - ), - ), - const SizedBox(width: 12), - - // INVOICE DATE - FormFieldBox( - label: "Invoice Date *", - child: GestureDetector( - onTap: () async { - final picked = await pickDate( - invoiceDate, - ); - if (picked != null) - setState( - () => invoiceDate = picked, - ); - }, - child: AbsorbPointer( - child: TextField( - readOnly: true, - decoration: - commonInputDecoration( - hint: df.format(invoiceDate), - ).copyWith( - suffixIcon: const Icon( - Icons.calendar_today, - size: 18, - ), - ), - ), - ), - ), - ), - const SizedBox(width: 12), - - // BROKER - FormFieldBox( - label: "Select Broker *", - child: DropdownButtonFormField( - value: selectedBrokerID, - decoration: commonInputDecoration( - hint: "Select Broker", - ), - items: filteredBrokerData.map((a) { - final int id = - int.tryParse( - a['id'].toString(), - ) ?? - 0; - return DropdownMenuItem( - value: id, - child: Text(a['name'].toString()), - ); - }).toList(), - onChanged: (id) => setState( - () => selectedBrokerID = id, - ), - ), - ), - const SizedBox(width: 12), - - // AGENT - FormFieldBox( - label: "Select Agent *", - child: DropdownButtonFormField( - value: selectedAgentId, - decoration: commonInputDecoration( - hint: "Select Agent", - ), - items: filteredAgentData.map((a) { - final int id = - int.tryParse( - a['id'].toString(), - ) ?? - 0; - return DropdownMenuItem( - value: id, - child: Text(a['name'].toString()), - ); - }).toList(), - onChanged: (id) => setState( - () => selectedAgentId = id, - ), - ), - ), - const SizedBox(width: 12), - - // POLICY TILL DATE - FormFieldBox( - label: "Policy Till Date *", - child: GestureDetector( - onTap: () async { - final picked = await pickDate( - policyTillDate!, - ); - if (picked != null) - setState( - () => policyTillDate = picked, - ); - }, - child: AbsorbPointer( - child: TextField( - readOnly: true, - decoration: - commonInputDecoration( - hint: df.format( - policyTillDate!, - ), - ).copyWith( - suffixIcon: const Icon( - Icons.calendar_today, - size: 18, - ), - ), - ), - ), - ), - ), - const SizedBox(width: 12), - - // --- RESET + SEARCH (BOTTOM ALIGNED) --- - Column( - mainAxisAlignment: - MainAxisAlignment.center, - children: [ - Row( - children: [ - // RESET - SizedBox( - height: 30, - width: 30, - child: OutlinedButton( - style: OutlinedButton.styleFrom( - padding: EdgeInsets.zero, - ), - onPressed: () { - setState(() { - selectedBrokerID = null; - selectedAgentId = null; - fromDate = null; - toDate = null; - policyTillDate = - DateTime.now(); - filteredPolicies = []; - selectedPolicies.clear(); - }); - }, - child: const Icon( - Icons.refresh, - size: 18, - ), - ), - ), - const SizedBox(width: 12), - - // SEARCH - SizedBox( - height: 30, - width: 30, - child: OutlinedButton( - style: OutlinedButton.styleFrom( - padding: EdgeInsets.zero, - ), - onPressed: loadPolicies, - child: const Icon( - Icons.search, - size: 18, - ), - ), - ), - ], - ), - ], - ), - ], - ), - ), - ], - ), + ), + // SizedBox(width: 10), + Spacer(), + Text( + 'Status: ${isPending ? 'Pending' : 'Completed'}', + style: GoogleFonts.inter( + fontSize: 11, + fontWeight: FontWeight.w300, + color: const Color(0xFF6B7280), + // color: isPending + // ? Colors.blueAccent + // : const Color(0xFF047857), ), ), ], - ), - ), - const SizedBox(height: 20), - - // ---------------------- - // Policy Selection - // ---------------------- - Card( - color: Colors.white, // White background - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(12), - ), - elevation: 1, - child: Column( - children: [ - ListTile( - // contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 4), - title: const Text("Policy Selection"), - // ---------------- RIGHT SIDE (filters + collapse) ---------------- - trailing: Row( - mainAxisSize: MainAxisSize.min, + if (!isEdit) ...[ + const Spacer(), + Container( + padding: const EdgeInsets.symmetric( + horizontal: 10, + vertical: 6, + ), + decoration: BoxDecoration( + color: const Color(0xFFECFDF5), // emerald bg + borderRadius: BorderRadius.circular(20), + ), + child: Row( children: [ - // FROM DATE - SizedBox( - height: 30, - width: MediaQuery.of(context).size.width * 0.12, - child: GestureDetector( - onTap: () async { - final date = await pickDate( - fromDate ?? DateTime.now(), - ); - if (date != null) - setState(() => fromDate = date); - }, - child: AbsorbPointer( - child: TextField( - readOnly: true, - decoration: - commonInputDecoration( - hint: "From Date", - ).copyWith( - // 👇 THIS is where you set formatted date - suffixIcon: const Icon( - Icons.calendar_today, - size: 18, - ), - labelText: fromDate == null - ? "From Date" - : df.format(fromDate!), - ), - ), - ), + Text( + "SELECTED POLICIES : ", + style: GoogleFonts.poppins( + fontSize: 10, + fontWeight: FontWeight.w500, + color: const Color(0xFF065F46), + letterSpacing: 0.4, ), ), - const SizedBox(width: 8), - - // TO DATE - SizedBox( - height: 30, - width: MediaQuery.of(context).size.width * 0.12, - child: GestureDetector( - onTap: () async { - final date = await pickDate( - toDate ?? DateTime.now(), - ); - if (date != null) - setState(() => toDate = date); - }, - child: AbsorbPointer( - child: TextField( - readOnly: true, - decoration: - commonInputDecoration( - hint: "To Date", - ).copyWith( - // 👇 THIS is where you set formatted date - suffixIcon: const Icon( - Icons.calendar_today, - size: 18, - ), - labelText: toDate == null - ? "To Date" - : df.format(toDate!), - ), - ), - ), - ), - ), - const SizedBox(width: 8), - - // RESET BUTTON - SizedBox( - height: 30, - width: 30, - child: OutlinedButton( - style: OutlinedButton.styleFrom( - padding: EdgeInsets.zero, - ), - onPressed: () { - setState(() { - fromDate = null; - toDate = null; - }); - loadPolicies(); - }, - child: const Icon(Icons.refresh, size: 18), - ), - ), - const SizedBox(width: 8), - - // SEARCH BUTTON - SizedBox( - height: 30, - width: 30, - child: OutlinedButton( - style: OutlinedButton.styleFrom( - padding: EdgeInsets.zero, - ), - onPressed: filterPolicies, - child: const Icon(Icons.search, size: 18), - ), - ), - const SizedBox(width: 8), - - // COLLAPSE ICON - GestureDetector( - onTap: () => setState( - () => policyCollapsed = !policyCollapsed, - ), - child: Icon( - policyCollapsed - ? Icons.expand_more - : Icons.expand_less, - color: Colors.teal, - size: 26, + const SizedBox(width: 6), + Text( + // "15", + // selectedPolicies.length.toString(), + totalPolicies, + style: GoogleFonts.poppins( + fontSize: 13, + fontWeight: FontWeight.w700, + color: const Color(0xFF047857), ), ), ], ), - onTap: () => - setState(() => policyCollapsed = !policyCollapsed), ), - // ------------------- HR LINE WHEN OPEN ------------------- - if (!policyCollapsed) - const Divider( - height: 1, - thickness: 1, - color: Color(0xFFE0E0E0), + + const SizedBox(width: 10), + + // 💰 TOTAL COMMISSION + Container( + padding: const EdgeInsets.symmetric( + horizontal: 12, + vertical: 6, ), - - AnimatedCrossFade( - duration: const Duration(milliseconds: 500), - crossFadeState: policyCollapsed - ? CrossFadeState.showFirst - : CrossFadeState.showSecond, - firstChild: Container(), - secondChild: Padding( - padding: const EdgeInsets.all(16), - child: Column( - children: [ - // ---------------- SELECT ALL ---------------- - if (filteredPolicies.isNotEmpty) - Container( - width: double.infinity, - padding: const EdgeInsets.symmetric( - vertical: 8, - horizontal: 16, - ), - child: Row( - children: [ - Checkbox( - value: - filteredPolicies.isNotEmpty && - filteredPolicies.every( - (p) => selectedPolicies.contains( - int.parse(p["policy_id"]), - ), - ), - onChanged: (v) { - setState(() { - if (v == true) { - selectedPolicies = - filteredPolicies - .map( - (p) => int.parse( - p["policy_id"], - ), - ) - .toSet(); - } else { - selectedPolicies.clear(); - } - }); - }, - ), - const SizedBox(width: 8), - const Text( - "Select All Policies", - style: TextStyle(fontSize: 16), - ), - ], - ), - ), - - // ---------------- POLICY LIST ---------------- - if (filteredPolicies.isEmpty) - const Padding( - padding: EdgeInsets.all(20), - child: Text("No policies found"), - ) - else - ...filteredPolicies.map((p) { - // final int id = int.parse(p["policy_id"]); - // final bool isSelected = selectedPolicies.contains(id); - - // ---- SAFE ID PARSING ---- - final String policyIdStr = - p["policy_id"]?.toString() ?? "0"; - final int id = int.tryParse(policyIdStr) ?? 0; - - final bool isSelected = selectedPolicies - .contains(id); - - // ---- SAFE FIELDS ---- - final String policyNo = - p["policy_no"]?.toString() ?? "-"; - final String customer = - p["customer_name"]?.toString() ?? "-"; - - final String premiumStr = - p["premium_amount"]?.toString() ?? "0"; - final double premium = - double.tryParse(premiumStr) ?? 0; - - final String commissionStr = - p["commission_amount"]?.toString() ?? "0"; - final double commission = - double.tryParse(commissionStr) ?? 0; - - return ListTile( - tileColor: isSelected - ? Colors.teal.shade50 - : null, - leading: Checkbox( - value: isSelected, - onChanged: (v) { - setState(() { - if (v == true) { - selectedPolicies.add(id); - } else { - selectedPolicies.remove(id); - } - }); - }, - ), - - title: Text(policyNo), - - subtitle: Text( - "Customer: ${customer} | " - "Premium: ₹${premium}", - ), - - trailing: Text( - "₹${commission}", - style: const TextStyle( - color: Colors.teal, - fontWeight: FontWeight.bold, - ), - ), - - onTap: () { - setState(() { - if (isSelected) { - selectedPolicies.remove(id); - } else { - selectedPolicies.add(id); - } - }); - }, - ); - }).toList(), - ], - ), + decoration: BoxDecoration( + color: const Color(0xFF10B981), // emerald green + borderRadius: BorderRadius.circular(22), + boxShadow: [ + BoxShadow( + color: const Color(0xFF10B981).withOpacity(0.25), + blurRadius: 6, + offset: const Offset(0, 2), + ), + ], + ), + child: Row( + children: [ + const Icon( + Icons.account_balance_wallet_outlined, + size: 14, + color: Colors.white, + ), + const SizedBox(width: 6), + Text( + "TOTAL COMMISSION :", + style: GoogleFonts.poppins( + fontSize: 10, + fontWeight: FontWeight.w500, + color: Colors.white.withOpacity(0.9), + letterSpacing: 0.4, + ), + ), + const SizedBox(width: 6), + Text( + // "₹${_calculateTotalCommission()}", + "₹$totalCommission", + style: GoogleFonts.poppins( + fontSize: 14, + fontWeight: FontWeight.w700, + color: Colors.white, + ), + ), + ], ), ), ], - ), - ), - - const SizedBox(height: 100), - ], - ), - ), - - // ------------------------------ - // Bottom Summary Bar - // ------------------------------ - if (selectedPolicies.isNotEmpty) - Positioned( - left: 0, - right: 0, - bottom: 0, - child: Material( - elevation: 8, - child: Container( - padding: const EdgeInsets.all(16), - color: Colors.white, - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - // ---------------- SELECTED COUNT ---------------- - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const Text( - "Selected Policies", - style: TextStyle(fontSize: 12), - ), - const SizedBox(height: 4), - Text( - selectedPolicies.length.toString(), - style: const TextStyle( - fontSize: 18, - fontWeight: FontWeight.bold, - ), - ), - ], - ), - - // ---------------- TOTAL COMMISSION ---------------- - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const Text( - "Total Commission", - style: TextStyle(fontSize: 12), - ), - const SizedBox(height: 4), - - /// NULL-SAFE TOTAL COMMISSION CALCULATION - Text( - "₹${_calculateTotalCommission()}", - style: const TextStyle( - fontSize: 18, - fontWeight: FontWeight.bold, - color: Colors.teal, - ), - ), - ], - ), - - // ---------------- BUTTONS ---------------- - Row( - children: [ - TextButton( - onPressed: () => - setState(() => selectedPolicies.clear()), - child: const Text("Clear"), - ), - const SizedBox(width: 10), - ElevatedButton( - onPressed: saveInvoice, - style: ElevatedButton.styleFrom( - backgroundColor: Colors.orange, - ), - child: const Text("Submit"), - ), - ], - ), - ], - ), + ], ), ), ), + SizedBox(height: 5), + + if (!isEdit) ...[ + Container( + padding: EdgeInsets.all(8.0), + child: DateFilterRowPayout( + formKey: _formKey, + // key: ValueKey(SelectedStatus ?? ''), + role: roleId, + id: userId, + selectedParnter: selectedAgentId, + selectedBroker: selectedBrokerID, + onBrokerChanged: (val) { + print('Selected Filterd SselectedBrokerID - $val'); + setState(() { + selectedBrokerID = val; // ✅ parent state updated + }); + }, + + onPartnerChanges: (val) { + setState(() { + selectedAgentId = val; // ✅ parent state updated + }); + }, + selectedStaffId: '', + startController: controllers['startDate']!, + endController: controllers['endDate']!, + + isMobile: ResponsiveLayout.isMobile(context), + onFilter: () { + print( + 'DATEROw- ${controllers['startDate']!} -${controllers['endDate']!}' + ' - $selectedAgentId - $selectedBrokerID', + ); + // call your filter logic + // filterDateRange(); + + loadPolicies(); + }, + onRefresh: () { + // call your refresh logic + refresh(); + }, + ), + ), + ], + // Policy Selection + Card( + // color: Colors.amber, + color: Colors.white, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), + ), + elevation: 0, + child: Column( + children: [ + AnimatedCrossFade( + duration: const Duration(milliseconds: 300), + crossFadeState: policyCollapsed + ? CrossFadeState.showFirst + : CrossFadeState.showSecond, + firstChild: const SizedBox(), + secondChild: Column( + children: [ + _policyTableHeader(), // 🔒 Static header + _policyTableBody(), // 🔽 Scrollable rows + ], + ), + ), + ], + ), + ), + + if (selectedPolicies.isNotEmpty) ...[ + Container( + padding: const EdgeInsets.all(8), + // color: Colors.amber, + color: Color(0xFFf9fafb), + // color: Colors.white, + child: Row( + // mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + // ---------------- SELECTED COUNT ---------------- + Row( + // crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + // selectedPolicies.length.toString(), + totalPolicies, + style: GoogleFonts.poppins( + fontSize: 12, + fontWeight: FontWeight.w500, + color: Colors.blueGrey, + ), + ), + const SizedBox(width: 4), + Text( + "policies selected", + style: GoogleFonts.poppins( + fontSize: 12, + fontWeight: FontWeight.w500, + color: Colors.blueGrey, + ), + ), + const SizedBox(width: 4), + Text( + "|", + style: GoogleFonts.poppins( + fontSize: 12, + fontWeight: FontWeight.w500, + color: Colors.blueGrey, + ), + ), + const SizedBox(width: 4), + Text( + "Total Commission", + style: GoogleFonts.poppins( + fontSize: 12, + fontWeight: FontWeight.w500, + color: Colors.blueGrey, + ), + ), + const SizedBox(width: 4), + + /// NULL-SAFE TOTAL COMMISSION CALCULATION + Text( + // "₹ ${_calculateTotalCommission()}", + "₹ $totalCommission", + style: GoogleFonts.poppins( + fontSize: 14, + fontWeight: FontWeight.w500, + color: Colors.teal, + ), + ), + ], + ), + + Spacer(), + buildPOS(context), + SizedBox(width: 20), + // ---------------- BUTTONS ---------------- + Row( + children: [ + ElevatedButton( + onPressed: saveInvoice, + style: ElevatedButton.styleFrom( + // backgroundColor: const Color(0xff45a049), + backgroundColor: Colors.teal, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular( + 4, + ), // 🔽 reduced radius + ), + padding: const EdgeInsets.symmetric( + horizontal: 16, + vertical: 10, + ), + ), + child: Text( + "Raise Invoice", + style: GoogleFonts.poppins( + color: Colors.white, + fontSize: 12, + fontWeight: FontWeight.w500, + ), + ), + ), + const SizedBox(width: 10), + TextButton( + onPressed: () => + setState(() => selectedPolicies.clear()), + child: const Text("Clear"), + ), + ], + ), + ], + ), + ), + ], + + if (isEdit) ...[ + Container( + padding: const EdgeInsets.all(8), + // color: Colors.amber, + color: Color(0xFFf9fafb), + // color: Colors.white, + child: Row( + mainAxisAlignment: MainAxisAlignment.end, + // mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + // selectedPolicies.length.toString(), + totalPolicies, + style: GoogleFonts.poppins( + fontSize: 12, + fontWeight: FontWeight.w500, + color: Colors.blueGrey, + ), + ), + const SizedBox(width: 4), + Text( + "policies selected", + style: GoogleFonts.poppins( + fontSize: 12, + fontWeight: FontWeight.w500, + color: Colors.blueGrey, + ), + ), + const SizedBox(width: 4), + Text( + "|", + style: GoogleFonts.poppins( + fontSize: 12, + fontWeight: FontWeight.w500, + color: Colors.blueGrey, + ), + ), + const SizedBox(width: 4), + Text( + "Total Commission", + style: GoogleFonts.poppins( + fontSize: 12, + fontWeight: FontWeight.w500, + color: Colors.blueGrey, + ), + ), + const SizedBox(width: 4), + + /// NULL-SAFE TOTAL COMMISSION CALCULATION + Text( + // "₹ ${_calculateTotalCommission()}", + "₹ $totalCommission", + style: GoogleFonts.poppins( + fontSize: 14, + fontWeight: FontWeight.w500, + color: Colors.teal, + ), + ), + + const SizedBox(width: 10), + ], + ), + ), + ], + // ---------------------- + // const SizedBox(height: 100), + ], + ), + ), + ); + } + + Widget _policyTableHeader() { + final allSelected = + filteredPolicies.isNotEmpty && + filteredPolicies.every( + (p) => selectedPolicies.contains( + int.tryParse(p["policy_id"]?.toString() ?? "0") ?? 0, + ), + ); + // filteredPolicies.every( + // (p) => selectedPolicies.contains( + // int.tryParse(p["policy_id"].toString()) ?? 0, + // ), + // ); + + final partiallySelected = selectedPolicies.isNotEmpty && !allSelected; + + return Container( + height: 30, + padding: const EdgeInsets.symmetric(horizontal: 16), + decoration: const BoxDecoration( + // color: Color(0xFFf9fafb), + color: Color(0xFFf9fafb), + // color: Color(0xFFf9fafb), + // color: Color(0xFFF6F8F7), + // border: Border(bottom: BorderSide(color: Color(0xFFE0E0E0))), + // border: Border(bottom: BorderSide(color: Color(0xFFECEFF1))), + border: Border(bottom: BorderSide(color: Color(0xFFf9fafb))), + ), + child: Row( + children: [ + if (!isEdit) ...[ + SizedBox( + width: colCheck, + child: Checkbox( + value: allSelected ? true : (partiallySelected ? null : false), + tristate: true, + onChanged: (v) { + setState(() { + if (v == true) { + selectedPolicies = filteredPolicies + .map( + (p) => + int.tryParse( + p["policy_id"]?.toString() ?? "0", + ) ?? + 0, + ) + .where((id) => id != 0) // Filter out invalid IDs + .toSet(); + // selectedPolicies = filteredPolicies + // .map( + // (p) => int.tryParse(p["policy_id"].toString()) ?? 0, + // ) + // .toSet(); + } else { + selectedPolicies.clear(); + } + totalPolicies = selectedPolicies.length.toString(); + }); + _calculateTotalCommission(); + }, + ), + ), + ], + _headerText("POLICY NO", colPolicy), + _headerText("POLICY DATE", colDate), + _headerText("REFERER", colAgent), + _headerText("CUSTOMER", colCustomer), + _headerText("PREMIUM", colPremium), + _headerText("COMMISSION", colCommission), ], ), ); } + Widget _headerText(String text, double width) { + return SizedBox( + width: width, + child: Text( + text, + style: GoogleFonts.poppins( + fontSize: 11, + fontWeight: FontWeight.w500, + color: Colors.blueGrey, + letterSpacing: 0.5, + ), + ), + ); + } + + Widget _policyTableBody() { + return SizedBox( + height: isEdit ? 400 : 340, // avoids overflow + child: ListView.builder( + itemCount: filteredPolicies.length, + itemBuilder: (context, index) { + final p = filteredPolicies[index]; + + final int id = int.tryParse(p["policy_id"]?.toString() ?? "0") ?? 0; + final bool isSelected = selectedPolicies.contains(id); + + return InkWell( + onTap: () { + setState(() { + isSelected + ? selectedPolicies.remove(id) + : selectedPolicies.add(id); + totalPolicies = selectedPolicies.length.toString(); + }); + _calculateTotalCommission(); + }, + child: Container( + height: 38, + padding: const EdgeInsets.symmetric(horizontal: 16), + decoration: BoxDecoration( + // const Color(0xFFE6F4EF) + color: isSelected ? const Color(0xFFF0FAF6) : Colors.white, + border: const Border( + bottom: BorderSide(color: Color(0xFFf9fafb)), + // bottom: BorderSide(color: Color(0xFFEDEDED)), + ), + ), + child: Row( + children: [ + if (!isEdit) ...[ + SizedBox( + width: colCheck, + child: Checkbox( + value: isSelected, + onChanged: (v) { + setState(() { + v == true + ? selectedPolicies.add(id) + : selectedPolicies.remove(id); + totalPolicies = selectedPolicies.length.toString(); + }); + _calculateTotalCommission(); + }, + ), + ), + ], + _cell(p["policy_no"], colPolicy), + // _cell(p["issued_date"] ?? "-", colDate), + _cell(_formatDate(p['issued_date']), colDate), + _cell(p["agent_name"] ?? "-", colAgent), + _cell(p["customer_name"], colCustomer), + _cell("₹${p["premium_amount"]}", colPremium), + SizedBox( + width: colCommission, + child: Text( + "₹${p["commission_amount"]}", + style: const TextStyle( + fontSize: 11, + fontWeight: FontWeight.w600, + color: Color(0xFF009B77), // Emerald green + ), + ), + ), + ], + ), + ), + ); + }, + ), + ); + } + + Widget _cell(String? value, double width) { + return SizedBox( + width: width, + child: Text( + value ?? "-", + overflow: TextOverflow.ellipsis, + style: const TextStyle(fontSize: 11), + ), + ); + } + double _calculateTotalCommission() { double total = 0.0; @@ -1040,11 +1229,165 @@ class _PayOutScreenState extends ConsumerState { total += comm; } - + setState(() { + totalCommission = total.roundToDouble(); + }); // Remove decimal points (.00) return total.roundToDouble(); } + Widget buildPOS(BuildContext context) { + Map? selectedPOSdata = filteredPOSData.firstWhere( + (item) => item['id'].toString() == selectedPOS, + orElse: () => {}, + ); + return Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Text('Select POS', style: _textStyle), + SizedBox(width: 5), + SizedBox( + height: 35, + width: MediaQuery.of(context).size.width * 0.15, + child: DropdownSearch>( + key: dropDownKeyPOS, + selectedItem: selectedPOSdata.isNotEmpty ? selectedPOSdata : null, + items: (filter, infiniteScrollProps) { + return filteredPOSData; + }, + + itemAsString: (val) => val['name'].toString(), // what to show + compareFn: (item, selectedItem) => + item['id'] == selectedItem['id'], // ✅ compare by id + // validator: (val) { + // if (val == null) { + // return "Required"; // ✅ error message + // } + // return null; + // }, + suffixProps: DropdownSuffixProps( + // make sure the dropdown button is visible + dropdownButtonProps: DropdownButtonProps( + isVisible: true, + padding: EdgeInsets.zero, // remove default padding + constraints: const BoxConstraints( + // shrink icon tap area + minWidth: 12, + minHeight: 12, + ), + iconSize: 15, // smaller icon + // icon: const Icon(Icons.arrow_drop_down), + ), + ), + dropdownBuilder: (context, selectedItem) => Align( + alignment: Alignment.centerLeft, + child: Text( + selectedItem != null ? selectedItem['name'].toString() : "", + style: GoogleFonts.poppins( + fontSize: 11, + color: Colors.black, + // color: Color(0XFF6366F1), + ), + overflow: TextOverflow.ellipsis, + maxLines: 1, + softWrap: false, + ), + ), + decoratorProps: DropDownDecoratorProps( + decoration: AppInputDecorations.dropdownDecoration(label: "POS") + .copyWith( + filled: true, + fillColor: + Colors.white, // 👈 makes the dropdown input white + isDense: true, + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(5), + borderSide: const BorderSide( + color: Color(0xFFE2E8F0), + width: 0.1, + ), + ), + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(5), + borderSide: const BorderSide( + color: Color(0xFFE2E8F0), + width: 0.5, + ), + ), + contentPadding: EdgeInsets.symmetric( + horizontal: 8, + vertical: 6, + ), + ), + ), + + popupProps: PopupProps.menu( + fit: FlexFit.loose, + constraints: BoxConstraints(maxHeight: 250), + menuProps: MenuProps( + backgroundColor: + Colors.white, // 👈 sets dropdown background to white + ), + showSearchBox: true, + searchFieldProps: TextFieldProps( + autofocus: true, + style: GoogleFonts.inter(fontSize: 11, color: Colors.black), + decoration: InputDecoration( + contentPadding: EdgeInsets.all(1), + filled: true, + fillColor: Colors.white, + hintText: "Search POS...", + hintStyle: GoogleFonts.inter( + fontSize: 12, + color: Colors.black, + ), + enabledBorder: OutlineInputBorder( + borderSide: BorderSide( + color: Colors.white, + ), // 👈 Normal border + ), + focusedBorder: OutlineInputBorder( + borderSide: BorderSide( + color: Colors.white, + width: 1.5, + ), // 👈 Focused border + ), + ), + ), + + itemBuilder: (context, item, isDisabled, isSelected) { + return Container( + // color: isSelected ? Colors.blue.withOpacity(0.1) : null, + padding: const EdgeInsets.symmetric( + horizontal: 8, + vertical: 3, + ), + child: Text( + item['name'].toString(), + style: GoogleFonts.inter(fontSize: 12, color: Colors.black), + ), + ); + }, + // constraints: BoxConstraints(), + ), + + onChanged: (val) { + if (val != null) { + print("Selected POS : ${val['name']}"); + print("Id: ${val['id']}"); + selectedPOS = val['id']; + + // widget.onBrokerChanged?.call(val['id']); + // controllers['agentId']?.text = val['agent_code']; + // agentId = agent['id']; + } + }, + ), + ), + ], + ); + } + InputDecoration commonInputDecoration({required String hint}) { return InputDecoration( hintText: hint, @@ -1067,4 +1410,28 @@ class _PayOutScreenState extends ConsumerState { ), ); } + + static final _textStyle = GoogleFonts.poppins( + fontSize: 11, + fontWeight: FontWeight.w500, + ); + + String _formatDate(String rawDate) { + try { + final dateTime = DateTime.parse(rawDate); + return DateFormat('dd-MM-yyyy').format(dateTime); // 24-hour format + } catch (e) { + return rawDate; // fallback if parsing fails + } + } + + static final double colCheck = 44; + double get colPolicy => isEdit ? 150 : 200; + double get colDate => isEdit ? 120 : 200; + static final double colAgent = 200; + static final double colCustomer = 200; + static final double colPremium = 200; + // static final double colCommission = 200; + + double get colCommission => isEdit ? 120 : 200; } diff --git a/lib/presentation/screens/staff/Enquiry/enquiry_inline_SubRow.dart b/lib/presentation/screens/staff/Enquiry/enquiry_inline_SubRow.dart index 095ca45..c8655b3 100644 --- a/lib/presentation/screens/staff/Enquiry/enquiry_inline_SubRow.dart +++ b/lib/presentation/screens/staff/Enquiry/enquiry_inline_SubRow.dart @@ -371,46 +371,62 @@ class _SubRowWidgetState extends ConsumerState { @override Widget build(BuildContext context) { - return Wrap( - spacing: 12, - runSpacing: 12, - children: [ - buildSelectStaffMem(context), - buildAgentName(context, fromHeader: false), - buildInsurer(context), - buildBroker(context), - buildInsuredName(context), - buildVehicleNumber(context), - buildEmail(context), - buildPhNumber(context), - buildRemarks(context), + return Form( + key: _formKey, + child: Wrap( + crossAxisAlignment: WrapCrossAlignment.center, + spacing: 12, + runSpacing: 12, + children: [ + buildSelectStaffMem(context), + buildAgentName(context, fromHeader: false), + buildInsurer(context), + buildBroker(context), + buildInsuredName(context), + buildVehicleNumber(context), + buildEmail(context), + buildPhNumber(context), + buildRemarks(context), - IconButton( - tooltip: 'Save', - padding: EdgeInsets.zero, - constraints: const BoxConstraints(minWidth: 24, minHeight: 24), - icon: const Icon( - Icons.check, - color: Colors.green, - size: 28, // smaller icon - ), - splashRadius: 14, // - onPressed: () => widget.onSave(widget.selectId), - ), + Row( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.end, - IconButton( - tooltip: 'Close', - padding: EdgeInsets.zero, - constraints: const BoxConstraints(minWidth: 24, minHeight: 24), - icon: const Icon( - Icons.close, - color: Colors.indigo, - size: 28, // smaller icon + children: [ + IconButton( + tooltip: 'Save', + padding: EdgeInsets.zero, + constraints: const BoxConstraints(minWidth: 14, minHeight: 24), + icon: const Icon( + Icons.check, + color: Colors.green, + size: 20, // smaller icon + ), + splashRadius: 14, // + onPressed: () { + if (!_formKey.currentState!.validate()) return; + + widget.onSave(widget.selectId); + }, + ), + + IconButton( + tooltip: 'Close', + padding: EdgeInsets.zero, + constraints: const BoxConstraints(minWidth: 14, minHeight: 24), + icon: const Icon( + Icons.close, + color: Colors.indigo, + size: 20, // smaller icon + ), + splashRadius: 14, // + onPressed: widget.onClose, + ), + ], ), - splashRadius: 14, // - onPressed: widget.onClose, - ), - ], + ], + ), ); } @@ -446,12 +462,12 @@ class _SubRowWidgetState extends ConsumerState { itemAsString: (val) => val['name'].toString(), // what to show compareFn: (item, selectedItem) => item['id'] == selectedItem['id'], // ✅ compare by id - validator: (val) { - if (val == null) { - return "Required"; // ✅ error message - } - return null; - }, + // validator: (val) { + // if (val == null) { + // return "Required"; // ✅ error message + // } + // return null; + // }, suffixProps: DropdownSuffixProps( // make sure the dropdown button is visible dropdownButtonProps: DropdownButtonProps( @@ -608,6 +624,10 @@ class _SubRowWidgetState extends ConsumerState { hintText: 'Vehicle Number', borderColor: Color(0XFF6366F1), textColr: Color(0XFF6366F1), + enableBorderWidth: 0.5, + bordedRad: 5, + errFieldHgt: 10, + errFieldFont: 0, isdense: true, inputFormatters: [ UpperCaseTextFormatter(), // 👈 custom formatter for uppercase @@ -662,7 +682,7 @@ class _SubRowWidgetState extends ConsumerState { // errorText: fieldErrors['name'], borderColor: Color(0XFF6366F1), isdense: true, - validator: (value) => Validators.requiredField(value, "name"), + // validator: (value) => Validators.requiredField(value, "name"), widthNone: true, ), ), @@ -678,11 +698,15 @@ class _SubRowWidgetState extends ConsumerState { SizedBox(height: 5), SizedBox( width: MediaQuery.of(context).size.width * 0.1, - child: ThemedFormInlineField( + child: ThemedFormField( controller: widget.controllers['email']!, borderColor: Color(0XFF6366F1), textColr: Color(0XFF6366F1), hintText: 'Email', + bordedRad: 5, + enableBorderWidth: 0.5, + // errFieldHgt: 10, + // errFieldFont: 0, isdense: true, validator: (value) => Validators.nonReqemail(value, "email"), inputFormatters: [ @@ -705,15 +729,17 @@ class _SubRowWidgetState extends ConsumerState { SizedBox(height: 5), SizedBox( width: MediaQuery.of(context).size.width * 0.1, - child: ThemedFormInlineField( + child: ThemedFormField( controller: widget.controllers['mobile']!, borderColor: Color(0XFF6366F1), textColr: Color(0XFF6366F1), hintText: 'Mobile', + enableBorderWidth: 0.5, + bordedRad: 5, isdense: true, validator: (value) => Validators.nonReqphone(value, "phone"), inputFormatters: [ - FilteringTextInputFormatter.allow(RegExp(r'[ 0-9]')), + FilteringTextInputFormatter.allow(RegExp(r'[0-9]')), ], txtwidth: ResponsiveLayout.isMobile(context) ? null @@ -737,6 +763,7 @@ class _SubRowWidgetState extends ConsumerState { controller: widget.controllers['remarks']!, borderColor: Color(0XFF6366F1), textColr: Color(0XFF6366F1), + bordedRad: 5, verticalPad: 10, horizonalPad: 10, isdense: true, @@ -785,12 +812,12 @@ class _SubRowWidgetState extends ConsumerState { 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; - }, + // validator: (val) { + // if (val == null) { + // return "Required"; // ✅ error message + // } + // return null; + // }, suffixProps: DropdownSuffixProps( // make sure the dropdown button is visible dropdownButtonProps: DropdownButtonProps( @@ -963,12 +990,12 @@ class _SubRowWidgetState extends ConsumerState { itemAsString: (val) => val['short_name'].toString(), // what to show compareFn: (item, selectedItem) => item['id'] == selectedItem['id'], // ✅ compare by id - validator: (val) { - if (val == null) { - return "Required"; // ✅ error message - } - return null; - }, + // validator: (val) { + // if (val == null) { + // return "Required"; // ✅ error message + // } + // return null; + // }, suffixProps: DropdownSuffixProps( // make sure the dropdown button is visible dropdownButtonProps: DropdownButtonProps( @@ -1117,12 +1144,12 @@ class _SubRowWidgetState extends ConsumerState { itemAsString: (val) => val['name'].toString(), // what to show compareFn: (item, selectedItem) => item['id'] == selectedItem['id'], // ✅ compare by id - validator: (val) { - if (val == null) { - return "Required"; // ✅ error message - } - return null; - }, + // validator: (val) { + // if (val == null) { + // return "Required"; // ✅ error message + // } + // return null; + // }, suffixProps: DropdownSuffixProps( // make sure the dropdown button is visible dropdownButtonProps: DropdownButtonProps( diff --git a/lib/presentation/screens/staff/Enquiry/enquiry_inline_list.dart b/lib/presentation/screens/staff/Enquiry/enquiry_inline_list.dart index 5856bd6..105eeb0 100644 --- a/lib/presentation/screens/staff/Enquiry/enquiry_inline_list.dart +++ b/lib/presentation/screens/staff/Enquiry/enquiry_inline_list.dart @@ -52,12 +52,14 @@ class EnquiryListStaffInline extends ConsumerStatefulWidget { class EnquiryStaffState extends ConsumerState { int currentPage = 1; int itemsPerPage = 10; + // int itemsPerPage = 5; int completedCurrentPage = 1; + // int completedItemsPerPage = 5; int completedItemsPerPage = 50; int totalCompletedPages = 1; List itemsPerPageOptions = [25, 50, 100, 200]; - + Timer? _debounce; late ApiService apiService; dynamic userId; dynamic idPrimary; @@ -389,21 +391,80 @@ class EnquiryStaffState extends ConsumerState { } // Add this getter method to calculate paginated completed data + // List> get paginatedCompletedData { + // if (completedData.isEmpty) return []; + // + // // Calculate total pages + // totalCompletedPages = (completedData.length / completedItemsPerPage).ceil(); + // + // // Calculate start and end indices + // final startIndex = (completedCurrentPage - 1) * completedItemsPerPage; + // final endIndex = (startIndex + completedItemsPerPage).clamp( + // 0, + // completedData.length, + // ); + // + // // Return paginated subset + // return completedData.sublist(startIndex, endIndex); + // } + + List> get paginatedCompletedData1 { + // If search is active (filteredData is different from completedData), use filteredData + // Otherwise use completedData + final dataToUse = filteredData; + + print("paginatedCompletedData - $dataToUse"); + + if (dataToUse.isEmpty) return []; + + // For completed tab only + if (selectedIndex == 1) { + // Calculate total pages based on current data + totalCompletedPages = (dataToUse.length / completedItemsPerPage).ceil(); + if (totalCompletedPages == 0) totalCompletedPages = 1; + + // Calculate start and end indices + final startIndex = (completedCurrentPage - 1) * completedItemsPerPage; + final endIndex = (startIndex + completedItemsPerPage).clamp( + 0, + dataToUse.length, + ); + + // Return paginated subset + return dataToUse.sublist(startIndex, endIndex); + } + + return dataToUse; + } + List> get paginatedCompletedData { - if (completedData.isEmpty) return []; + final dataToUse = filteredData; - // Calculate total pages - totalCompletedPages = (completedData.length / completedItemsPerPage).ceil(); + if (dataToUse.isEmpty) return []; - // Calculate start and end indices - final startIndex = (completedCurrentPage - 1) * completedItemsPerPage; - final endIndex = (startIndex + completedItemsPerPage).clamp( - 0, - completedData.length, - ); + if (selectedIndex == 1) { + totalCompletedPages = (dataToUse.length / completedItemsPerPage) + .ceil() + .clamp(1, double.infinity) + .toInt(); - // Return paginated subset - return completedData.sublist(startIndex, endIndex); + final startIndex = (completedCurrentPage - 1) * completedItemsPerPage; + + // 🚨 Guard: page out of range after search + if (startIndex >= dataToUse.length) { + completedCurrentPage = 1; + return dataToUse.take(completedItemsPerPage).toList(); + } + + final endIndex = (startIndex + completedItemsPerPage).clamp( + 0, + dataToUse.length, + ); + + return dataToUse.sublist(startIndex, endIndex); + } + + return dataToUse; } Future autoRefrshfilterDateRange() async { @@ -647,6 +708,10 @@ class EnquiryStaffState extends ConsumerState { ? List.from(inProgressData) : List.from(completedData); + if (SelectedStatus == 'Completed') { + selectedIndex = 1; + } + print('filteredData count: ${filteredData.length}'); print('inProgressData count: ${inProgressData.length}'); print('completedData count: ${completedData.length}'); @@ -671,7 +736,180 @@ class EnquiryStaffState extends ConsumerState { } } + String safeString(dynamic value) { + if (value == null) return ''; + return value.toString(); + } + void filterData(String query) { + _debounce?.cancel(); + _debounce = Timer(const Duration(milliseconds: 300), () { + _applyFilter(query); + }); + } + + void _applyFilter(String query) { + print("FilterData - $query"); + + setState(() { + final sourceList = (selectedIndex == 0) ? inProgressData : completedData; + + final q = query.trim().toLowerCase(); + + completedCurrentPage = 1; + + if (q.length < 2) { + filteredData = List.from(sourceList); + return; + } + + filteredData = sourceList.where((item) { + return safeString(item['agent_name']).toLowerCase().contains(q) || + safeString(item['agent_code']).toLowerCase().contains(q) || + safeString(item['reg_no']).toLowerCase().contains(q) || + safeString(item['insured_name']).toLowerCase().contains(q) || + safeString(item['policy_number']).toLowerCase().contains(q) || + safeString(item['broker_name']).toLowerCase().contains(q) || + safeString(item['insurer_name']).toLowerCase().contains(q) || + safeString(item['insurer_short_name']).toLowerCase().contains(q) || + safeString(item['enquiry_status']).toLowerCase().contains(q) || + safeString(item['assigned_to_name']).toLowerCase().contains(q) || + safeString(item['status']).toLowerCase().contains(q) || + safeString(item['payment_mode']).toLowerCase().contains(q) || + safeString(item['premium_amount']).toLowerCase().contains(q) || + safeString( + _formatDate(item['created_on']), + ).toLowerCase().contains(q) || + safeString( + _formatDate(item['updated_on']), + ).toLowerCase().contains(q); + }).toList(); + + print("FilterData2 - ${filteredData.length}"); + }); + } + + // void _applyFilter(String query) { + // print("FilterData - $query"); + // setState(() { + // // ALWAYS search from the FULL source list, not current filtered results + // final sourceList = (selectedIndex == 0) ? inProgressData : completedData; + // + // final q = query.trim().toLowerCase(); + // + // if (q.length < 2) { + // filteredData = List.from(sourceList); + // return; + // } + // + // if (query.isEmpty) { + // // If search is empty, show all data for current tab + // filteredData = List.from(sourceList); + // + // print("FilterData1 - $filteredData"); + // } else { + // // Search within ALL data from the current tab's source + // filteredData = sourceList.where((item) { + // final searchLower = query.toLowerCase(); + // + // return safeString( + // item['agent_name'], + // ).toLowerCase().contains(searchLower) || + // safeString( + // item['agent_code'], + // ).toLowerCase().contains(searchLower) || + // safeString(item['reg_no']).toLowerCase().contains(searchLower) || + // safeString( + // item['insurer_name'], + // ).toLowerCase().contains(searchLower) || + // safeString( + // item['enquiry_status'], + // ).toLowerCase().contains(searchLower) || + // safeString( + // item['assigned_to_name'], + // ).toLowerCase().contains(searchLower) || + // safeString( + // item['insured_name'], + // ).toLowerCase().contains(searchLower) || + // safeString( + // item['insurer_short_name'], + // ).toLowerCase().contains(searchLower) || + // safeString( + // item['premium_amount']?.toString(), + // ).contains(searchLower) || + // safeString( + // item['payment_mode'], + // ).toLowerCase().contains(searchLower) || + // safeString( + // item['broker_name'], + // ).toLowerCase().contains(searchLower) || + // safeString( + // _formatDate(item['created_on']), + // ).toLowerCase().contains(q) || + // safeString( + // _formatDate(item['updated_on']), + // ).toLowerCase().contains(q) || + // safeString( + // item['policy_number'], + // ).toLowerCase().contains(searchLower) || + // safeString(item['status']).toLowerCase().contains(searchLower); + // + // // return safeString( + // // item['agent_name'] ?? '-', + // // ).toLowerCase().contains(searchLower) || + // // safeString( + // // item['agent_code'] ?? '-', + // // ).toLowerCase().contains(searchLower) || + // // safeString( + // // item['reg_no'] ?? '-', + // // ).toLowerCase().contains(searchLower) || + // // safeString( + // // item['insurer_name'] ?? '-', + // // ).toLowerCase().contains(searchLower) || + // // safeString( + // // item['enquiry_status'] ?? '-', + // // ).toLowerCase().contains(searchLower) || + // // safeString( + // // item['assigned_to_name'] ?? '-', + // // ).toLowerCase().contains(searchLower) || + // // safeString( + // // item['insured_name'] ?? '-', + // // ).toLowerCase().contains(searchLower) || + // // safeString( + // // item['insurer_short_name'] ?? '-', + // // ).toLowerCase().contains(searchLower) || + // // safeString( + // // item['premium_amount'] ?? '-', + // // ).toString().toLowerCase().contains(searchLower) || + // // safeString( + // // item['payment_mode'] ?? '-', + // // ).toLowerCase().contains(searchLower) || + // // safeString( + // // item['broker_name'] ?? '-', + // // ).toLowerCase().contains(searchLower) || + // // safeString( + // // _formatDate(item['created_on']), + // // ).toLowerCase().contains(searchLower) || + // // safeString( + // // _formatDate(item['updated_on']), + // // ).toLowerCase().contains(searchLower) || + // // safeString( + // // item['policy_number'] ?? '-', + // // ).toLowerCase().contains(searchLower) || + // // safeString( + // // item['status'] ?? '-', + // // ).toLowerCase().contains(searchLower); + // }).toList(); + // print("FilterData2 - $filteredData"); + // // Reset to page 1 when searching in completed tab + // if (selectedIndex == 1) { + // completedCurrentPage = 1; + // } + // } + // }); + // } + + void filterData1(String query) { print("FilterData - $query"); setState(() { // Get the correct source list based on selected tab @@ -730,6 +968,10 @@ class EnquiryStaffState extends ConsumerState { ); }).toList(); } + + if (selectedIndex == 1) { + completedCurrentPage = 1; + } }); } @@ -1537,6 +1779,17 @@ class EnquiryStaffState extends ConsumerState { ), SizedBox(width: 10), MouseRegion( + onEnter: (_) { + setState(() { + isActionable = true; + }); + }, + onExit: (_) { + setState(() { + // isActionable = false; + isActionable = _searchStaffController.text.isNotEmpty; + }); + }, child: ThemedSearchField( hintText: 'Search', backgroundColor: Color(0xFFFFFFFF), @@ -1614,49 +1867,13 @@ class EnquiryStaffState extends ConsumerState { 'Assigned': 'Assigned', 'In progress': 'In Progress', }; - - List> get filteredDataByTab1 { - if (selectedIndex == 0) { - // Progress enquiries - final sortedData = [...inProgressData] - ..sort((a, b) => int.parse(b['id']) - int.parse(a['id'])); - - // final sortedData = [...filteredData] - // ..sort((a, b) => int.parse(b['id']) - int.parse(a['id'])); - - if (sortedData.isEmpty) return []; - return sortedData.where((e) { - return [ - "To be assigned", - "Assigned", - "In progress", - ].contains(e['enquiry_status']); - }).toList(); - } else { - // final sortedData = [...filteredData] - // ..sort((a, b) => int.parse(b['id']) - int.parse(a['id'])); - - final sortedData = [...completedData] - ..sort((a, b) => int.parse(b['id']) - int.parse(a['id'])); - - if (sortedData.isEmpty) return []; - // Completed enquiries - return paginatedCompletedData; - // return sortedData.where((e) { - // return e['enquiry_status'] == "Completed"; - // }).toList(); - } - } - List> get filteredDataByTab { - // Use filteredData instead of the raw source lists final sortedData = [...filteredData] ..sort((a, b) => int.parse(b['id']) - int.parse(a['id'])); if (sortedData.isEmpty) return []; if (selectedIndex == 0) { - // Progress enquiries - return filtered in-progress items return sortedData.where((e) { return [ "To be assigned", @@ -1664,26 +1881,10 @@ class EnquiryStaffState extends ConsumerState { "In progress", ].contains(e['enquiry_status']); }).toList(); - } else { - // Completed enquiries - apply pagination to filtered completed items - final completedFiltered = sortedData.where((e) { - return e['enquiry_status'] == "Completed"; - }).toList(); - - // Update total pages based on filtered results - totalCompletedPages = (completedFiltered.length / completedItemsPerPage) - .ceil(); - if (totalCompletedPages == 0) totalCompletedPages = 1; - - // Apply pagination - final startIndex = (completedCurrentPage - 1) * completedItemsPerPage; - final endIndex = (startIndex + completedItemsPerPage).clamp( - 0, - completedFiltered.length, - ); - - return completedFiltered.sublist(startIndex, endIndex); } + + // Completed tab → ALWAYS paginate + return paginatedCompletedData; } // Add group enquiries by status @@ -2647,7 +2848,12 @@ class EnquiryStaffState extends ConsumerState { : '-', style: _dataBold, ), - Text(_formatDate(item['updated_on']), style: _dataBoldthm4), + Text( + item['updated_on'] != null + ? _formatDate(item['updated_on']) + : '-', + style: _dataBoldthm4, + ), ], ), ), @@ -2828,10 +3034,15 @@ class EnquiryStaffState extends ConsumerState { setState(() { print('TAPPED TAB'); selectedIndex = index; + filteredData = (index == 0) ? inProgressData : completedData; if (index == 1) { completedCurrentPage = 1; + // final newValue = false; + // isActionable = newValue; } + + _searchStaffController.clear(); }); }, child: AnimatedContainer( @@ -3079,6 +3290,129 @@ class EnquiryStaffState extends ConsumerState { if (selectedIndex != 1 || completedData.length <= 25) { return SizedBox.shrink(); } + // if (selectedIndex != 1 || completedData.length <= 25) { + // return SizedBox.shrink(); + // } + + // Calculate display values based on filteredData + final totalItems = filteredData.length; + final startItem = ((completedCurrentPage - 1) * completedItemsPerPage) + 1; + final endItem = (completedCurrentPage * completedItemsPerPage).clamp( + 0, + totalItems, + ); + + return Container( + padding: EdgeInsets.symmetric(vertical: 2, horizontal: 16), + decoration: BoxDecoration( + color: Colors.white, + border: Border(top: BorderSide(color: Color(0xFFE2E8F0))), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Row( + children: [ + Text( + 'Showing $startItem-$endItem of $totalItems', // ← Changed this + style: GoogleFonts.inter( + fontSize: 10, + color: Color(0xFF64748B), + ), + ), + SizedBox(width: 16), + // Items per page dropdown + Container( + padding: EdgeInsets.symmetric(horizontal: 8), + decoration: BoxDecoration( + border: Border.all(color: Color(0xFFE2E8F0)), + borderRadius: BorderRadius.circular(6), + ), + child: DropdownButton( + value: completedItemsPerPage, + underline: SizedBox(), + isDense: true, + items: itemsPerPageOptions.map((int value) { + return DropdownMenuItem( + value: value, + child: Text( + '$value / page', + style: GoogleFonts.inter( + fontSize: 10, + color: Color(0xFF334155), + ), + ), + ); + }).toList(), + onChanged: (int? newValue) { + if (newValue != null) { + setState(() { + completedItemsPerPage = newValue; + completedCurrentPage = 1; + }); + } + }, + ), + ), + ], + ), + // Pagination buttons remain the same + Row( + children: [ + IconButton( + icon: Icon(Icons.first_page, size: 18), + onPressed: completedCurrentPage > 1 + ? () => setState(() => completedCurrentPage = 1) + : null, + color: completedCurrentPage > 1 + ? Color(0xFF2E7D6E) + : Colors.grey, + tooltip: 'First page', + ), + IconButton( + icon: Icon(Icons.chevron_left, size: 18), + onPressed: completedCurrentPage > 1 + ? () => setState(() => completedCurrentPage--) + : null, + color: completedCurrentPage > 1 + ? Color(0xFF2E7D6E) + : Colors.grey, + tooltip: 'Previous page', + ), + ..._buildPageNumbers(), + IconButton( + icon: Icon(Icons.chevron_right, size: 18), + onPressed: completedCurrentPage < totalCompletedPages + ? () => setState(() => completedCurrentPage++) + : null, + color: completedCurrentPage < totalCompletedPages + ? Color(0xFF2E7D6E) + : Colors.grey, + tooltip: 'Next page', + ), + IconButton( + icon: Icon(Icons.last_page, size: 18), + onPressed: completedCurrentPage < totalCompletedPages + ? () => setState( + () => completedCurrentPage = totalCompletedPages, + ) + : null, + color: completedCurrentPage < totalCompletedPages + ? Color(0xFF2E7D6E) + : Colors.grey, + tooltip: 'Last page', + ), + ], + ), + ], + ), + ); + } + + Widget _buildPaginationControls1() { + if (selectedIndex != 1 || completedData.length <= 25) { + return SizedBox.shrink(); + } return Container( padding: EdgeInsets.symmetric(vertical: 2, horizontal: 16), @@ -3093,7 +3427,7 @@ class EnquiryStaffState extends ConsumerState { Row( children: [ Text( - 'Showing ${((completedCurrentPage - 1) * completedItemsPerPage) + 1}-${(completedCurrentPage * completedItemsPerPage).clamp(0, completedData.length)} of ${completedData.length}', + 'Showing ${((completedCurrentPage - 1) * completedItemsPerPage) + 1}-${(completedCurrentPage * completedItemsPerPage).clamp(0, filteredData.length)} of ${filteredData.length}', style: GoogleFonts.inter( fontSize: 10, color: Color(0xFF64748B), diff --git a/lib/presentation/screens/staff/Enquiry/raise_enq_form.dart b/lib/presentation/screens/staff/Enquiry/raise_enq_form.dart index 3c90dfd..b8ba064 100644 --- a/lib/presentation/screens/staff/Enquiry/raise_enq_form.dart +++ b/lib/presentation/screens/staff/Enquiry/raise_enq_form.dart @@ -423,6 +423,7 @@ class RaiseEnqFormState extends ConsumerState { buildVehicleNumber(context), Row( mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.end, children: [ buildRefresh(context), SizedBox(width: 5), diff --git a/lib/presentation/screens/staff/policy/policy_list.dart b/lib/presentation/screens/staff/policy/policy_list.dart index 96376b8..d4a71a8 100644 --- a/lib/presentation/screens/staff/policy/policy_list.dart +++ b/lib/presentation/screens/staff/policy/policy_list.dart @@ -775,7 +775,7 @@ class policylistState extends ConsumerState { context.push(AppRoutes.policyValidation, extra: item); }, child: Container( - width: 110, // ⭐ FIXED WIDTH → Equal in both states + width: 80, // ⭐ FIXED WIDTH → Equal in both states padding: const EdgeInsets.symmetric( horizontal: 8, vertical: 4, @@ -787,8 +787,9 @@ class policylistState extends ConsumerState { borderRadius: BorderRadius.circular(5), ), child: Row( + mainAxisSize: MainAxisSize.min, mainAxisAlignment: - MainAxisAlignment.center, // ⭐ Center content + MainAxisAlignment.spaceEvenly, // ⭐ Center content children: [ Icon( item['is_data_accuracy_checked'] == '1' @@ -797,12 +798,12 @@ class policylistState extends ConsumerState { size: 12, color: Colors.white, ), - const SizedBox(width: 4), + const SizedBox(width: 1), Text( item['is_data_accuracy_checked'] == '1' ? 'Verified' - : 'Click to Verify', - style: const TextStyle( + : 'To Verify', + style: GoogleFonts.poppins( color: Colors.white, fontSize: 10, fontWeight: FontWeight.w500, diff --git a/lib/presentation/screens/staff/policy/policy_validation.dart b/lib/presentation/screens/staff/policy/policy_validation.dart index 2bd8e2f..d14971d 100644 --- a/lib/presentation/screens/staff/policy/policy_validation.dart +++ b/lib/presentation/screens/staff/policy/policy_validation.dart @@ -1,6 +1,8 @@ +import 'package:dropdown_search/dropdown_search.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:http/http.dart' as http; import 'package:pdf_render/pdf_render_widgets.dart'; import 'package:syncfusion_flutter_pdfviewer/pdfviewer.dart'; @@ -10,56 +12,13 @@ import '../../../../core/services/api_service.dart'; import '../../../../data/utils/toastNotification.dart'; import '../../../providers/manager_provider.dart'; +import '../../../themes/indicators/input_field_decoration.dart'; import 'loadNetworkPdfBytes.dart'; import 'dart:typed_data'; -// /// Represents Homepage for Navigation -// class policyValidation extends ConsumerStatefulWidget { -// final dynamic item; -// const policyValidation({Key? key, this.item}) : super(key: key); -// -// @override -// ConsumerState createState() => _policyValidationState(); -// } -// -// class _policyValidationState extends ConsumerState { -// final GlobalKey _pdfViewerKey = GlobalKey(); -// -// @override -// void initState() { -// super.initState(); -// } -// -// @override -// Widget build(BuildContext context) { -// return Scaffold( -// appBar: AppBar( -// title: const Text('Syncfusion Flutter PDF Viewer'), -// actions: [ -// IconButton( -// icon: const Icon( -// Icons.bookmark, -// color: Colors.white, -// semanticLabel: 'Bookmark', -// ), -// onPressed: () { -// _pdfViewerKey.currentState?.openBookmarkView(); -// }, -// ), -// ], -// ), -// body: SfPdfViewer.asset( -// 'assets/pdfs/sample.pdf', -// key: _pdfViewerKey, -// ) -// ); -// } -// } import 'package:flutter/services.dart'; -final digitsOnlyFormatter = [ - FilteringTextInputFormatter.digitsOnly, -]; +final digitsOnlyFormatter = [FilteringTextInputFormatter.digitsOnly]; final decimalFormatter = [ FilteringTextInputFormatter.allow(RegExp(r'^\d*\.?\d{0,2}$')), @@ -83,43 +42,195 @@ class _policyValidationState extends ConsumerState { String? pdfUrl; // full file path from API // --- Controllers (all editable fields) - late TextEditingController policyNumberController; - late TextEditingController insuredNameController; - late TextEditingController premiumController; - late TextEditingController remarksController; + final List controllerKeys = [ + // Policy + 'policyNumber', + 'insuredName', + 'premiumAmount', + 'remarks', - late TextEditingController rcNoController; - late TextEditingController issuedDateController; - late TextEditingController startDateController; - late TextEditingController endDateController; + // Dates + 'issuedDate', + 'startDate', + 'endDate', - late TextEditingController tpController; - late TextEditingController odController; - late TextEditingController paController; - late TextEditingController cgstController; - late TextEditingController sgstController; - late TextEditingController igstController; + // Tax + 'tp', + 'od', + 'pa', + 'cgst', + 'sgst', + 'igst', - late TextEditingController rtoStateCodeController; - late TextEditingController rtoCityCodeController; - late TextEditingController weightController; - late TextEditingController fuelTypeController; - late TextEditingController registrationDateController; - late TextEditingController yomController; + // Vehicle + 'rcNo', + 'rtoStateCode', + 'rtoCityCode', + 'weight', + 'fuelType', + 'dateOfRegistration', + 'yearOfManufacture', + 'engineNo', + 'chassisNo', + 'make', + 'model', + 'cubicCapacity', + 'vehicleType', - late TextEditingController engineNoController; - late TextEditingController chassisNoController; - late TextEditingController makeController; - late TextEditingController modelController; - late TextEditingController cubicCapacityController; - late TextEditingController vehicleTypeController; + // Broker + 'broker_name', + 'commission_amount', - late TextEditingController brokerNameController; - late TextEditingController commissionAmountController; + //find Policy + 'insurance_plan_type', + ]; + late Map controllers; + String? selectedInsurancPlanType; + String? selectedInsuranceId; + String? selectedAgentRentionRate; + String? selectedManagerRentionRate; + + List getVehicleTypeData = []; + List filteredVehicleTypeData = []; + + List getFuelTypeData = []; + List filteredFuelTypeData = []; final GlobalKey _pdfViewerKey = GlobalKey(); final _formKey = GlobalKey(); + String? selectedVehicleType; + String? selectedFuelType; + + final GlobalKey>> dropDownKey = + GlobalKey>>(); + + final GlobalKey>> dropDownKeyFuel = + GlobalKey>>(); + + Map dataDetails() { + return { + "rc_no": controllers["rcNo"]?.text, + "insured_name": controllers["insuredName"]?.text, + "issued_date": controllers["issuedDate"]?.text, + "start_date": controllers["startDate"]?.text, + "end_date": controllers["endDate"]?.text, + "premium_amount": controllers["premiumAmount"]?.text, + "policy_number": controllers["policyNumber"]?.text, + + "tp": controllers["tp"]?.text?.isEmpty ?? true + ? null + : controllers["tp"]?.text, + + "od": controllers["od"]?.text?.isEmpty ?? true + ? null + : controllers["od"]?.text, + + "pa": controllers["pa"]?.text?.isEmpty ?? true + ? null + : controllers["pa"]?.text, + + "cgst": controllers["cgst"]?.text?.isEmpty ?? true + ? null + : controllers["cgst"]?.text, + + "sgst": controllers["sgst"]?.text?.isEmpty ?? true + ? null + : controllers["sgst"]?.text, + + "igst": controllers["igst"]?.text?.isEmpty ?? true + ? null + : controllers["igst"]?.text, + + "rto_state_code": controllers["rtoStateCode"]?.text?.isEmpty ?? true + ? null + : controllers["rtoStateCode"]?.text, + + "rto_city_code": controllers["rtoCityCode"]?.text?.isEmpty ?? true + ? null + : controllers["rtoCityCode"]?.text, + + "weight": controllers["weight"]?.text?.isEmpty ?? true + ? null + : controllers["weight"]?.text, + + "fuel_type": controllers["fuelType"]?.text?.isEmpty ?? true + ? null + : controllers["fuelType"]?.text, + + "date_of_registration": + controllers["dateOfRegistration"]?.text?.isEmpty ?? true + ? null + : controllers["dateOfRegistration"]?.text, + + "year_of_manufacture": + controllers["yearOfManufacture"]?.text.isEmpty ?? true + ? null + : controllers["yearOfManufacture"]?.text, + + "engine_no": controllers["engineNo"]?.text?.isEmpty ?? true + ? null + : controllers["engineNo"]?.text, + + "chassis_no": controllers["chassisNo"]?.text?.isEmpty ?? true + ? null + : controllers["chassisNo"]?.text, + + "make": controllers["make"]?.text?.isEmpty ?? true + ? null + : controllers["make"]?.text, + + "model": controllers["model"]?.text?.isEmpty ?? true + ? null + : controllers["model"]?.text, + + "cubic_capacity": controllers["cubicCapacity"]?.text?.isEmpty ?? true + ? null + : controllers["cubicCapacity"]?.text, + + "vehicle_type": controllers["vehicleType"]?.text?.isEmpty ?? true + ? null + : controllers["vehicleType"]?.text, + + "broker_name": controllers["broker_name"]?.text?.isEmpty ?? true + ? null + : controllers["broker_name"]?.text, + + "commission_amount": + controllers["commission_amount"]?.text?.isEmpty ?? true + ? null + : controllers["commission_amount"]?.text, + }; + } + + Map buildCommissionPayload() { + final data = dataDetails(); + final policyId = widget.item?['policy_id']; + + return { + "vehicle_type": data["vehicle_type"], + "insurance_plan_type": selectedInsurancPlanType, + "premium_amount": data["premium_amount"], + "od": data["od"], + "tp": data["tp"], + "cubic_capacity": data["cubic_capacity"], + "weight": data["weight"], + "make": data["make"], + "model": data["model"], + "year_of_manufacture": data["year_of_manufacture"], + "vehicle_age": '', + "date_of_registration": data["date_of_registration"], + "fuel_type": data["fuel_type"], + "rto_state_code": data["rto_state_code"], + "rto_city_code": data["rto_city_code"], + "issued_date": data["issued_date"], + "insurer_id": selectedInsuranceId, + "agent_retention_rate": selectedAgentRentionRate, + "manager_retention_rate": selectedManagerRentionRate, + "id": policyId, + "updated_by": userId, + }; + } @override void initState() { @@ -127,78 +238,10 @@ class _policyValidationState extends ConsumerState { apiService = ApiService(); // instantiate controllers with defaults - policyNumberController = TextEditingController(); - insuredNameController = TextEditingController(); - premiumController = TextEditingController(); - remarksController = TextEditingController(); - - rcNoController = TextEditingController(); - issuedDateController = TextEditingController(); - startDateController = TextEditingController(); - endDateController = TextEditingController(); - - tpController = TextEditingController(); - odController = TextEditingController(); - paController = TextEditingController(); - cgstController = TextEditingController(); - sgstController = TextEditingController(); - igstController = TextEditingController(); - - rtoStateCodeController = TextEditingController(); - rtoCityCodeController = TextEditingController(); - weightController = TextEditingController(); - fuelTypeController = TextEditingController(); - registrationDateController = TextEditingController(); - yomController = TextEditingController(); - - engineNoController = TextEditingController(); - chassisNoController = TextEditingController(); - makeController = TextEditingController(); - modelController = TextEditingController(); - cubicCapacityController = TextEditingController(); - vehicleTypeController = TextEditingController(); - - brokerNameController = TextEditingController(); - commissionAmountController = TextEditingController(); - - // Pre-fill controllers from initial `widget.item` (if available) - final item = widget.item as Map? ?? {}; - policyNumberController.text = item['policy_number']?.toString() ?? ''; - insuredNameController.text = item['insured_name']?.toString() ?? ''; - premiumController.text = item['premium_amount']?.toString() ?? ''; - remarksController.text = item['remarks']?.toString() ?? ''; - - rcNoController.text = item['rc_no']?.toString() ?? ''; - issuedDateController.text = item['issued_date']?.toString() ?? ''; - startDateController.text = item['start_date']?.toString() ?? ''; - endDateController.text = item['end_date']?.toString() ?? ''; - - tpController.text = item['tp']?.toString() ?? ''; - odController.text = item['od']?.toString() ?? ''; - paController.text = item['pa']?.toString() ?? ''; - cgstController.text = item['cgst']?.toString() ?? ''; - sgstController.text = item['sgst']?.toString() ?? ''; - igstController.text = item['igst']?.toString() ?? ''; - - rtoStateCodeController.text = item['rto_state_code']?.toString() ?? ''; - rtoCityCodeController.text = item['rto_city_code']?.toString() ?? ''; - weightController.text = item['weight']?.toString() ?? ''; - fuelTypeController.text = item['fuel_type']?.toString() ?? ''; - registrationDateController.text = - item['date_of_registration']?.toString() ?? ''; - yomController.text = item['year_of_manufacture']?.toString() ?? ''; - - engineNoController.text = item['engine_no']?.toString() ?? ''; - chassisNoController.text = item['chassis_no']?.toString() ?? ''; - makeController.text = item['make']?.toString() ?? ''; - modelController.text = item['model']?.toString() ?? ''; - cubicCapacityController.text = item['cubic_capacity']?.toString() ?? ''; - vehicleTypeController.text = item['vehicle_type']?.toString() ?? ''; - - brokerNameController.text = item['broker_name']?.toString() ?? ''; - commissionAmountController.text = - item['commission_amount']?.toString() ?? ''; - + controllers = { + for (final key in controllerKeys) key: TextEditingController(), + }; + update(); // read providers + call APIs Future.microtask(() { try { @@ -213,49 +256,86 @@ class _policyValidationState extends ConsumerState { } getFindPolicy(); + getVehicleType(); + getFuelType(); getPolicyFilePath(); }); } @override void dispose() { - policyNumberController.dispose(); - insuredNameController.dispose(); - premiumController.dispose(); - remarksController.dispose(); - - rcNoController.dispose(); - issuedDateController.dispose(); - startDateController.dispose(); - endDateController.dispose(); - - tpController.dispose(); - odController.dispose(); - paController.dispose(); - cgstController.dispose(); - sgstController.dispose(); - igstController.dispose(); - - rtoStateCodeController.dispose(); - rtoCityCodeController.dispose(); - weightController.dispose(); - fuelTypeController.dispose(); - registrationDateController.dispose(); - yomController.dispose(); - - engineNoController.dispose(); - chassisNoController.dispose(); - makeController.dispose(); - modelController.dispose(); - cubicCapacityController.dispose(); - vehicleTypeController.dispose(); - - brokerNameController.dispose(); - commissionAmountController.dispose(); + for (final controller in controllers.values) { + controller.dispose(); + } super.dispose(); } + void update() { + final item = widget.item as Map? ?? {}; + + // Prefill from API / passed data + item.forEach((key, value) { + if (controllers.containsKey(key)) { + controllers[key]!.text = value?.toString() ?? ''; + } + }); + } + + Future getVehicleType() async { + print('getvehicleType called'); + setState(() => isLoading = true); + + try { + final response = await apiService.fetchPolicyMasterDropDown( + 'vehicleType', + ); + + if (response['status'] == 'success' && response['data'] != null) { + print('Vehicle Types: ${response['data']}'); + setState(() { + getVehicleTypeData = List.from(response['data']); + filteredVehicleTypeData = List.from(getVehicleTypeData); + }); + + print('Vehicle Types: $getVehicleTypeData'); + } else { + getVehicleTypeData.clear(); + filteredVehicleTypeData.clear(); + } + } catch (e) { + print('Exception occurred: $e'); + } finally { + setState(() => isLoading = false); + } + } + + Future getFuelType() async { + print('getfuelType called'); + setState(() => isLoading = true); + + try { + final response = await apiService.fetchPolicyMasterDropDown('fuelType'); + + if (response['status'] == 'success' && response['data'] != null) { + print('fuelType Types: ${response['data']}'); + setState(() { + getFuelTypeData = List.from(response['data']); + filteredFuelTypeData = List.from(getFuelTypeData); + }); + + print('fuelType Types: $getFuelTypeData'); + } else { + getFuelTypeData.clear(); + filteredFuelTypeData.clear(); + } + } catch (e) { + print('Exception occurred: $e'); + } finally { + setState(() => isLoading = false); + } + } + Future getPolicyFilePath() async { _pdfViewerKey.currentState?.openBookmarkView(); @@ -277,7 +357,6 @@ class _policyValidationState extends ConsumerState { pdfUrl = "$url"; }); print("FINAL PDF URL -> $pdfUrl"); - } catch (e, st) { print('Exception in getPolicyFilePath: $e\n$st'); } finally { @@ -286,6 +365,23 @@ class _policyValidationState extends ConsumerState { } } + String fixInvalidDate(dynamic value) { + if (value == null) return ''; + final str = value.toString(); + + // Check for your invalid date patterns + if (str.contains('--0001') || str == '30-11--0001' || str == '0000-00-00') { + return ''; + } + return str; + } + + String safeText(dynamic value, {String fallback = ''}) { + if (value == null) return fallback; + final text = value.toString().trim(); + return text.isEmpty ? fallback : text; + } + Future getFindPolicy() async { final policyId = widget.item?['policy_id']; if (policyId == null) { @@ -302,68 +398,56 @@ class _policyValidationState extends ConsumerState { if (response != null && response['status'] == 'success') { final data = (response['data'] ?? {}) as Map; - + debugPrint('findPolicyApi response1:'); if (!mounted) return; + debugPrint('findPolicyApi response2:'); setState(() { - // update controllers (overwrite with authoritative API values) - policyNumberController.text = - data['policy_number']?.toString() ?? policyNumberController.text; - insuredNameController.text = - data['insured_name']?.toString() ?? insuredNameController.text; - premiumController.text = - data['premium_amount']?.toString() ?? premiumController.text; - remarksController.text = - data['remarks']?.toString() ?? remarksController.text; + controllers["policyNumber"]!.text = safeText(data['policy_number']); + controllers["insuredName"]!.text = safeText(data['insured_name']); + controllers["premiumAmount"]!.text = safeText(data['premium_amount']); + controllers["rcNo"]!.text = safeText(data['rc_no']); + // controllers["issuedDate"]!.text = safeText(data['issued_date']); + // controllers["startDate"]!.text = safeText(data['start_date']); + // controllers["endDate"]!.text = safeText(data['end_date']); - rcNoController.text = - data['rc_no']?.toString() ?? rcNoController.text; - issuedDateController.text = - data['issued_date']?.toString() ?? issuedDateController.text; - startDateController.text = - data['start_date']?.toString() ?? startDateController.text; - endDateController.text = - data['end_date']?.toString() ?? endDateController.text; + controllers["issueDate"]?.text = fixInvalidDate(data['issued_date']); + controllers["startDate"]?.text = fixInvalidDate(data['start_date']); + controllers["endDate"]?.text = fixInvalidDate(data['end_date']); - tpController.text = data['tp']?.toString() ?? tpController.text; - odController.text = data['od']?.toString() ?? odController.text; - paController.text = data['pa']?.toString() ?? paController.text; - cgstController.text = data['cgst']?.toString() ?? cgstController.text; - sgstController.text = data['sgst']?.toString() ?? sgstController.text; - igstController.text = data['igst']?.toString() ?? igstController.text; + controllers["tp"]!.text = safeText(data['tp']); + controllers["od"]!.text = safeText(data['od']); + controllers["pa"]!.text = safeText(data['pa']); + controllers["cgst"]!.text = safeText(data['cgst']); + controllers["sgst"]!.text = safeText(data['sgst']); + controllers["igst"]!.text = safeText(data['igst']); - rtoStateCodeController.text = - data['rto_state_code']?.toString() ?? rtoStateCodeController.text; - rtoCityCodeController.text = - data['rto_city_code']?.toString() ?? rtoCityCodeController.text; - weightController.text = - data['weight']?.toString() ?? weightController.text; - fuelTypeController.text = - data['fuel_type']?.toString() ?? fuelTypeController.text; - registrationDateController.text = - data['date_of_registration']?.toString() ?? - registrationDateController.text; - yomController.text = - data['year_of_manufacture']?.toString() ?? yomController.text; + controllers["rtoStateCode"]!.text = safeText(data['rto_state_code']); + controllers["rtoCityCode"]!.text = safeText(data['rto_city_code']); + controllers["weight"]!.text = safeText(data['weight']); + controllers["dateOfRegistration"]!.text = safeText( + data['date_of_registration'], + ); - engineNoController.text = - data['engine_no']?.toString() ?? engineNoController.text; - chassisNoController.text = - data['chassis_no']?.toString() ?? chassisNoController.text; - makeController.text = data['make']?.toString() ?? makeController.text; - modelController.text = - data['model']?.toString() ?? modelController.text; - cubicCapacityController.text = - data['cubic_capacity']?.toString() ?? - cubicCapacityController.text; - vehicleTypeController.text = - data['vehicle_type']?.toString() ?? vehicleTypeController.text; + selectedFuelType = safeText(data['fuel_type']); + selectedVehicleType = safeText(data['vehicle_type']); + controllers["engineNo"]!.text = safeText(data['engine_no']); + controllers["chassisNo"]!.text = safeText(data['chassis_no']); + controllers["make"]!.text = safeText(data['make']); + controllers["model"]!.text = safeText(data['model']); - brokerNameController.text = - data['broker_name']?.toString() ?? brokerNameController.text; - commissionAmountController.text = - data['commission_amount']?.toString() ?? - commissionAmountController.text; + controllers["cubicCapacity"]!.text = safeText(data['cubic_capacity']); + + controllers["yearOfManufacture"]!.text = safeText( + data['year_of_manufacture'], + ); + + selectedInsurancPlanType = safeText(data['insurance_plan_type']); + selectedInsuranceId = safeText(data['insurer_id']); + selectedAgentRentionRate = safeText(data['agent_retention_rate']); + selectedManagerRentionRate = safeText(data['manager_retention_rate']); }); + + debugPrint('findPolicyApi response3:'); } else { debugPrint( 'findPolicyApi returned error: ${response?['message'] ?? 'unknown'}', @@ -407,6 +491,7 @@ class _policyValidationState extends ConsumerState { } Future _save() async { + print('SAVE'); final policyId = widget.item?['policy_id']; if (policyId == null) { @@ -414,7 +499,6 @@ class _policyValidationState extends ConsumerState { return; } - if (!_formKey.currentState!.validate()) { ToastHelper.showWarningToast(context, "Please fill all required fields"); return; @@ -425,60 +509,7 @@ class _policyValidationState extends ConsumerState { return; } - - final payload = { - "id": policyId, - "rc_no": rcNoController.text, - "insured_name": insuredNameController.text, - "issued_date": issuedDateController.text, - "start_date": startDateController.text, - "end_date": endDateController.text, - "premium_amount": premiumController.text, - "policy_number": policyNumberController.text, - "tp": tpController.text.isEmpty ? null : tpController.text, - "od": odController.text.isEmpty ? null : odController.text, - "pa": paController.text.isEmpty ? null : paController.text, - "cgst": cgstController.text.isEmpty ? null : cgstController.text, - "sgst": sgstController.text.isEmpty ? null : sgstController.text, - "igst": igstController.text.isEmpty ? null : igstController.text, - "rto_state_code": rtoStateCodeController.text.isEmpty - ? null - : rtoStateCodeController.text, - "rto_city_code": rtoCityCodeController.text.isEmpty - ? null - : rtoCityCodeController.text, - "weight": weightController.text.isEmpty ? null : weightController.text, - "fuel_type": fuelTypeController.text.isEmpty - ? null - : fuelTypeController.text, - "date_of_registration": registrationDateController.text.isEmpty - ? null - : registrationDateController.text, - "year_of_manufacture": yomController.text.isEmpty - ? null - : yomController.text, - "engine_no": engineNoController.text.isEmpty - ? null - : engineNoController.text, - "chassis_no": chassisNoController.text.isEmpty - ? null - : chassisNoController.text, - "make": makeController.text.isEmpty ? null : makeController.text, - "model": modelController.text.isEmpty ? null : modelController.text, - "cubic_capacity": cubicCapacityController.text.isEmpty - ? null - : cubicCapacityController.text, - "vehicle_type": vehicleTypeController.text.isEmpty - ? null - : vehicleTypeController.text, - "broker_name": brokerNameController.text.isEmpty - ? null - : brokerNameController.text, - "commission_amount": commissionAmountController.text.isEmpty - ? null - : commissionAmountController.text, - "updated_by": userId, - }; + final payload = {"id": policyId, ...dataDetails(), "updated_by": userId}; if (!mounted) return; setState(() => isLoading = true); @@ -516,6 +547,288 @@ class _policyValidationState extends ConsumerState { } } + Future _fetchCommision() async { + print('_fetchCommision'); + final policyId = widget.item?['policy_id']; + final payload = buildCommissionPayload(); + if (policyId == null) { + ToastHelper.showWarningToast(context, 'Missing policy id'); + return; + } + + if (!_formKey.currentState!.validate()) { + ToastHelper.showWarningToast(context, "Please fill all required fields"); + return; + } + + // if (widget.item?['is_data_accuracy_checked'] == "1") { + // ToastHelper.showWarningToast(context, "Data already confirmed"); + // return; + // } + + // final payload = { + // "id": policyId, + // 'insurance_plan_type': controllers['insurance_plan_type'], + // "updated_by": userId, + // ...dataDetails(), + // // "insurance_plan_type": + // }; + print('_fetchCommision1'); + // setState(() { + // isLoading = true; + // payload['insurance_plan_type'] = selectedInsurancPlanType; + // payload['insurer_id'] = selectedInsuranceId; + // payload['id'] = policyId; + // payload['updated_by'] = userId; + // print('payload2 - ${payload['insurance_plan_type']}'); + // print('payload - $payload'); + // }); + print('_fetchCommision2'); + try { + debugPrint('FINAL SAVE PAYLOAD 1=> $payload'); + final result = await apiService.calculateCommissionRequest(payload); + debugPrint('updatePolicy result => $result'); + + if (result != null && result['status'] == 'success') { + if (!mounted) return; + setState(() => isLoading = false); + // context.pop(); + ToastHelper.showSuccessToast(context, "Policy updated successfully"); + // Extract commission_amount from response + final commissionAmount = result['data']?['commission_amount']; + if (commissionAmount != null) { + controllers["commission_amount"]?.text = commissionAmount + .toStringAsFixed(2); + } + + // WidgetsBinding.instance.addPostFrameCallback((_) { + // // Now safe to do navigation + provider updates + // final container = ProviderScope.containerOf(context); + // container.read(policyDataAcurancyRefreshProvider.notifier).state = + // true; + // }); + } else { + if (!mounted) return; + setState(() => isLoading = false); + ToastHelper.showErrorToast( + context, + result?['message']?.toString() ?? 'Error updating policy', + ); + } + } catch (e, st) { + debugPrint('Exception in updatePolicy: $e\n$st'); + if (!mounted) return; + setState(() => isLoading = false); + ToastHelper.showWarningToast(context, 'Exception: $e'); + } + } + + Widget buildVehicleType(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + // mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text("Vehicle Type", style: TextStyle(fontWeight: FontWeight.w500)), + const SizedBox(height: 6), + DropdownSearch( + key: dropDownKey, + + selectedItem: selectedVehicleType?.isNotEmpty == true + ? selectedVehicleType + : null, + + items: (filter, infiniteScrollProps) { + return filteredVehicleTypeData; + }, + + itemAsString: (val) => val, // 👈 String directly + + validator: (val) { + if (val == null || val.isEmpty) { + return "Required"; + } + return null; + }, + + decoratorProps: DropDownDecoratorProps( + decoration: + AppInputDecorations.dropdownDecoration( + label: "Select Vehicle Type", + ).copyWith( + filled: true, + fillColor: Colors.white, + isDense: true, + + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(6), + borderSide: const BorderSide( + // color: Color(0xFFE2E8F0), + width: 0.6, + ), + ), + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(6), + borderSide: const BorderSide( + // color: Color(0xFFE2E8F0), + width: 0.6, + ), + ), + errorBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(6), + borderSide: const BorderSide(color: Colors.red), + ), + focusedErrorBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(6), + borderSide: const BorderSide(color: Colors.red), + ), + contentPadding: const EdgeInsets.symmetric( + horizontal: 8, + vertical: 10, + ), + ), + ), + + popupProps: PopupProps.menu( + fit: FlexFit.loose, + constraints: const BoxConstraints(maxHeight: 250), + menuProps: const MenuProps(backgroundColor: Colors.white), + showSearchBox: true, + + searchFieldProps: TextFieldProps( + autofocus: true, + decoration: InputDecoration( + contentPadding: const EdgeInsets.all(6), + filled: true, + fillColor: Colors.white, + hintText: "Search Vehicle Type...", + hintStyle: GoogleFonts.inter(fontSize: 12, color: Colors.black), + enabledBorder: const OutlineInputBorder( + borderSide: BorderSide(color: Color(0xFFE2E8F0)), + ), + focusedBorder: const OutlineInputBorder( + borderSide: BorderSide(color: Color(0xFFE2E8F0), width: 1.5), + ), + ), + ), + ), + + onChanged: (val) { + if (val != null) { + setState(() { + selectedVehicleType = val; + controllers['vehicleType']?.text = val; + }); + + print("Selected Vehicle Type: $val"); + } + }, + ), + ], + ); + } + + Widget buildFuelType(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text("Fuel Type ", style: TextStyle(fontWeight: FontWeight.w500)), + const SizedBox(width: 6), + DropdownSearch( + key: dropDownKeyFuel, + + selectedItem: selectedFuelType?.isNotEmpty == true + ? selectedFuelType + : null, + + items: (filter, infiniteScrollProps) { + return filteredFuelTypeData; + }, + + itemAsString: (val) => val, // 👈 String directly + + validator: (val) { + if (val == null || val.isEmpty) { + return "Required"; + } + return null; + }, + + decoratorProps: DropDownDecoratorProps( + decoration: + AppInputDecorations.dropdownDecoration( + label: "Select Fuel Type", + ).copyWith( + filled: true, + fillColor: Colors.white, + isDense: true, + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(6), + borderSide: const BorderSide( + // color: Color(0xFFE2E8F0), + width: 0.6, + ), + ), + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(6), + borderSide: const BorderSide( + // color: Color(0xFFE2E8F0), + width: 0.6, + ), + ), + errorBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(6), + borderSide: const BorderSide(color: Colors.red), + ), + focusedErrorBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(6), + borderSide: const BorderSide(color: Colors.red), + ), + contentPadding: const EdgeInsets.symmetric( + horizontal: 8, + vertical: 10, + ), + ), + ), + + popupProps: PopupProps.menu( + fit: FlexFit.loose, + constraints: const BoxConstraints(maxHeight: 250), + menuProps: const MenuProps(backgroundColor: Colors.white), + showSearchBox: true, + + searchFieldProps: TextFieldProps( + autofocus: true, + decoration: InputDecoration( + contentPadding: const EdgeInsets.all(6), + filled: true, + fillColor: Colors.white, + hintText: "Search Fuel Type...", + hintStyle: GoogleFonts.inter(fontSize: 12, color: Colors.black), + enabledBorder: const OutlineInputBorder( + borderSide: BorderSide(color: Color(0xFFE2E8F0)), + ), + focusedBorder: const OutlineInputBorder( + borderSide: BorderSide(color: Color(0xFFE2E8F0), width: 1.5), + ), + ), + ), + ), + + onChanged: (val) { + if (val != null) { + setState(() { + selectedFuelType = val; + controllers['fuelType']?.text = val; + }); + + print("Selected Fuel Type: $val"); + } + }, + ), + ], + ); + } + Widget _sectionHeader(String title, {IconData icon = Icons.info}) { return Container( padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 12), @@ -544,14 +857,14 @@ class _policyValidationState extends ConsumerState { } Widget _buildInput( - String label, - TextEditingController controller, { - TextInputType keyboardType = TextInputType.text, - bool readOnly = false, - VoidCallback? onTap, - bool required = false, - List? inputFormatters, // 👈 ADD THIS - }) { + String label, + TextEditingController controller, { + TextInputType keyboardType = TextInputType.text, + bool readOnly = false, + VoidCallback? onTap, + bool required = false, + List? inputFormatters, // 👈 ADD THIS + }) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -593,9 +906,6 @@ class _policyValidationState extends ConsumerState { ); } - - - @override Widget build(BuildContext context) { // Ensure global AppHeader remains visible by pushing content down by 64px @@ -612,6 +922,30 @@ class _policyValidationState extends ConsumerState { ElevatedButton( onPressed: widget.item?['is_data_accuracy_checked'] == '1' ? null + : _fetchCommision, + style: ElevatedButton.styleFrom( + backgroundColor: const Color(0xFF2E7D6E), + foregroundColor: Colors.white, + padding: const EdgeInsets.symmetric( + vertical: 10, + horizontal: 20, // 👉 smaller width + ), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + ), + ), + child: const Text( + 'Fetch Commission', + style: TextStyle(fontSize: 14, fontWeight: FontWeight.w600), + ), + ), + SizedBox(width: 10), + ElevatedButton( + // onPressed: widget.item?['is_data_accuracy_checked'] == '1' + onPressed: + widget.item?['is_data_accuracy_checked'] != '1' || + (controllers['commission_amount']?.text.isEmpty ?? true) + ? null : _save, style: ElevatedButton.styleFrom( backgroundColor: const Color(0xFF2E7D6E), @@ -695,7 +1029,7 @@ class _policyValidationState extends ConsumerState { crossAxisAlignment: CrossAxisAlignment.stretch, children: [ const Text( - 'Orginal Policy Document', + 'Original Policy Document', style: TextStyle( fontSize: 16, fontWeight: FontWeight.w600, @@ -766,16 +1100,20 @@ class _policyValidationState extends ConsumerState { child: SingleChildScrollView( child: Form( key: _formKey, - autovalidateMode: AutovalidateMode.onUserInteraction, + autovalidateMode: + AutovalidateMode.onUserInteraction, child: Card( elevation: 2, color: Colors.white, margin: EdgeInsets.zero, - shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + ), child: Padding( padding: const EdgeInsets.all(16), child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, + crossAxisAlignment: + CrossAxisAlignment.stretch, children: [ // Policy Details Card( @@ -788,7 +1126,7 @@ class _policyValidationState extends ConsumerState { padding: const EdgeInsets.all(12.0), child: Column( crossAxisAlignment: - CrossAxisAlignment.stretch, + CrossAxisAlignment.stretch, children: [ _sectionHeader( 'Policy Details', @@ -800,14 +1138,16 @@ class _policyValidationState extends ConsumerState { Expanded( child: _buildInput( 'Policy Number', - policyNumberController, required: true, + controllers['policyNumber']!, + required: true, ), ), const SizedBox(width: 8), Expanded( child: _buildInput( 'Insured Name', - insuredNameController, required: true + controllers['insuredName']!, + required: true, ), ), ], @@ -817,10 +1157,11 @@ class _policyValidationState extends ConsumerState { Expanded( child: _buildInput( 'Issued Date', - issuedDateController, - readOnly: true, required: true, + controllers['issuedDate']!, + readOnly: true, + required: true, onTap: () => _pickDate( - issuedDateController, + controllers['issuedDate']!, context, ), ), @@ -829,10 +1170,12 @@ class _policyValidationState extends ConsumerState { Expanded( child: _buildInput( 'Start Date', - startDateController, - readOnly: true, required: true, + controllers['startDate']!, + + readOnly: true, + required: true, onTap: () => _pickDate( - startDateController, + controllers['startDate']!, context, ), ), @@ -841,10 +1184,11 @@ class _policyValidationState extends ConsumerState { Expanded( child: _buildInput( 'End Date', - endDateController, - readOnly: true, required: true, + controllers['endDate']!, + readOnly: true, + required: true, onTap: () => _pickDate( - endDateController, + controllers['endDate']!, context, ), ), @@ -856,16 +1200,23 @@ class _policyValidationState extends ConsumerState { Expanded( child: _buildInput( 'Premium Amount', - premiumController,required: true, - keyboardType: TextInputType.numberWithOptions(decimal: true), - inputFormatters: decimalFormatter, + controllers['premiumAmount']!, + + required: true, + keyboardType: + TextInputType.numberWithOptions( + decimal: true, + ), + inputFormatters: + decimalFormatter, ), ), const SizedBox(width: 8), Expanded( child: _buildInput( 'Remarks', - remarksController,required: false + required: false, + controllers['remarks']!, ), ), ], @@ -888,7 +1239,7 @@ class _policyValidationState extends ConsumerState { padding: const EdgeInsets.all(12.0), child: Column( crossAxisAlignment: - CrossAxisAlignment.stretch, + CrossAxisAlignment.stretch, children: [ _sectionHeader( 'Tax / Cover Details', @@ -900,27 +1251,42 @@ class _policyValidationState extends ConsumerState { Expanded( child: _buildInput( 'TP', - tpController,required: true, - keyboardType: TextInputType.numberWithOptions(decimal: true), - inputFormatters: decimalFormatter, + controllers['tp']!, + required: true, + keyboardType: + TextInputType.numberWithOptions( + decimal: true, + ), + inputFormatters: + decimalFormatter, ), ), const SizedBox(width: 8), Expanded( child: _buildInput( 'OD', - odController,required: true, - keyboardType: TextInputType.numberWithOptions(decimal: true), - inputFormatters: decimalFormatter, + controllers['od']!, + required: true, + keyboardType: + TextInputType.numberWithOptions( + decimal: true, + ), + inputFormatters: + decimalFormatter, ), ), const SizedBox(width: 8), Expanded( child: _buildInput( 'PA', - paController,required: true, - keyboardType: TextInputType.numberWithOptions(decimal: true), - inputFormatters: decimalFormatter, + controllers['pa']!, + required: true, + keyboardType: + TextInputType.numberWithOptions( + decimal: true, + ), + inputFormatters: + decimalFormatter, ), ), ], @@ -930,27 +1296,42 @@ class _policyValidationState extends ConsumerState { Expanded( child: _buildInput( 'CGST', - cgstController,required: true, - keyboardType: TextInputType.numberWithOptions(decimal: true), - inputFormatters: decimalFormatter, + controllers['cgst']!, + required: true, + keyboardType: + TextInputType.numberWithOptions( + decimal: true, + ), + inputFormatters: + decimalFormatter, ), ), const SizedBox(width: 8), Expanded( child: _buildInput( 'SGST', - sgstController,required: true, - keyboardType: TextInputType.numberWithOptions(decimal: true), - inputFormatters: decimalFormatter, + controllers['sgst']!, + required: true, + keyboardType: + TextInputType.numberWithOptions( + decimal: true, + ), + inputFormatters: + decimalFormatter, ), ), const SizedBox(width: 8), Expanded( child: _buildInput( 'IGST', - igstController,required: true, - keyboardType: TextInputType.numberWithOptions(decimal: true), - inputFormatters: decimalFormatter, + controllers['igst']!, + required: true, + keyboardType: + TextInputType.numberWithOptions( + decimal: true, + ), + inputFormatters: + decimalFormatter, ), ), ], @@ -973,37 +1354,45 @@ class _policyValidationState extends ConsumerState { padding: const EdgeInsets.all(12.0), child: Column( crossAxisAlignment: - CrossAxisAlignment.stretch, + CrossAxisAlignment.stretch, children: [ _sectionHeader( 'Vehicle Details', icon: Icons.directions_car, ), + SizedBox(height: 10), Row( children: [ Expanded( child: _buildInput( 'RTO State Name', - rtoStateCodeController,required: false, + controllers['rtoStateCode']!, + required: true, ), ), const SizedBox(width: 8), Expanded( child: _buildInput( 'RTO City Code', - rtoCityCodeController,required: false, - keyboardType: TextInputType.number, - inputFormatters: digitsOnlyFormatter, + controllers['rtoCityCode']!, + required: true, + keyboardType: + TextInputType.number, + inputFormatters: + digitsOnlyFormatter, ), ), const SizedBox(width: 8), Expanded( child: _buildInput( 'Weight', - weightController,required: false, - keyboardType: TextInputType.number, - inputFormatters: digitsOnlyFormatter, + controllers['weight']!, + required: true, + keyboardType: + TextInputType.number, + inputFormatters: + digitsOnlyFormatter, ), ), ], @@ -1011,19 +1400,22 @@ class _policyValidationState extends ConsumerState { Row( children: [ Expanded( - child: _buildInput( - 'Fuel Type', - fuelTypeController,required: false, - ), + child: buildFuelType(context), + // child: _buildInput( + // 'Fuel Type', + // controllers['fuelType']!, + // required: true, + // ), ), const SizedBox(width: 8), Expanded( child: _buildInput( 'Date of Registration', - registrationDateController,required: false, + controllers['dateOfRegistration']!, + required: true, readOnly: true, onTap: () => _pickDate( - registrationDateController, + controllers['dateOfRegistration']!, context, ), ), @@ -1032,9 +1424,12 @@ class _policyValidationState extends ConsumerState { Expanded( child: _buildInput( 'Year of Manufacture', - yomController,required: false, - keyboardType: TextInputType.number, - inputFormatters: digitsOnlyFormatter, + controllers['yearOfManufacture']!, + required: true, + keyboardType: + TextInputType.number, + inputFormatters: + digitsOnlyFormatter, ), ), ], @@ -1044,14 +1439,16 @@ class _policyValidationState extends ConsumerState { Expanded( child: _buildInput( 'Engine No', - engineNoController,required: false, + controllers['engineNo']!, + required: true, ), ), const SizedBox(width: 8), Expanded( child: _buildInput( 'Chassis No', - chassisNoController,required: false, + controllers['chassisNo']!, + required: true, ), ), const SizedBox(width: 8), @@ -1059,7 +1456,8 @@ class _policyValidationState extends ConsumerState { child: Expanded( child: _buildInput( 'Make', - makeController,required: false, + controllers['make']!, + required: true, ), ), ), @@ -1070,23 +1468,33 @@ class _policyValidationState extends ConsumerState { Expanded( child: _buildInput( 'Model', - modelController,required: false, + controllers['model']!, + required: true, ), ), const SizedBox(width: 8), Expanded( child: _buildInput( 'Cubic Capacity', - cubicCapacityController,required: false, - keyboardType: TextInputType.numberWithOptions(decimal: true), - inputFormatters: decimalFormatter, + controllers['cubicCapacity']!, + required: true, + keyboardType: + TextInputType.numberWithOptions( + decimal: true, + ), + inputFormatters: + decimalFormatter, ), ), const SizedBox(width: 8), Expanded( - child: _buildInput( - 'Vehicle Type', - vehicleTypeController,required: false, + // child: _buildInput( + // 'Vehicle Type', + // controllers['vehicleType']!, + // required: true, + // ), + child: buildVehicleType( + context, ), ), ], @@ -1109,7 +1517,7 @@ class _policyValidationState extends ConsumerState { padding: const EdgeInsets.all(12.0), child: Column( crossAxisAlignment: - CrossAxisAlignment.stretch, + CrossAxisAlignment.stretch, children: [ _sectionHeader( 'Broker / Commission', @@ -1121,16 +1529,22 @@ class _policyValidationState extends ConsumerState { Expanded( child: _buildInput( 'Broker Name', - brokerNameController,required: false, + controllers['broker_name']!, + required: true, ), ), const SizedBox(width: 8), Expanded( child: _buildInput( 'Commission Amount', - commissionAmountController,required: false, - keyboardType: TextInputType.numberWithOptions(decimal: true), - inputFormatters: decimalFormatter, + controllers['commission_amount']!, + // required: true, + keyboardType: + TextInputType.numberWithOptions( + decimal: true, + ), + inputFormatters: + decimalFormatter, ), ), ], @@ -1149,7 +1563,7 @@ class _policyValidationState extends ConsumerState { ), ), ), - ) + ), ], ), ), @@ -1160,4 +1574,9 @@ class _policyValidationState extends ConsumerState { ), ); } + + static final _textStyle = GoogleFonts.poppins( + fontSize: 12, + fontWeight: FontWeight.w500, + ); } diff --git a/lib/presentation/screens/staff/policy/policy_validation_old.dart b/lib/presentation/screens/staff/policy/policy_validation_old.dart new file mode 100644 index 0000000..72dca5a --- /dev/null +++ b/lib/presentation/screens/staff/policy/policy_validation_old.dart @@ -0,0 +1,1355 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:go_router/go_router.dart'; +import 'package:http/http.dart' as http; +import 'package:pdf_render/pdf_render_widgets.dart'; +import 'package:syncfusion_flutter_pdfviewer/pdfviewer.dart'; + +import '../../../../core/config/env.dart'; +import '../../../../core/services/api_service.dart'; +import '../../../../data/utils/toastNotification.dart'; +import '../../../providers/manager_provider.dart'; + +import 'loadNetworkPdfBytes.dart'; +import 'dart:typed_data'; + +// /// Represents Homepage for Navigation +// class policyValidation extends ConsumerStatefulWidget { +// final dynamic item; +// const policyValidation({Key? key, this.item}) : super(key: key); +// +// @override +// ConsumerState createState() => _policyValidationState(); +// } +// +// class _policyValidationState extends ConsumerState { +// final GlobalKey _pdfViewerKey = GlobalKey(); +// +// @override +// void initState() { +// super.initState(); +// } +// +// @override +// Widget build(BuildContext context) { +// return Scaffold( +// appBar: AppBar( +// title: const Text('Syncfusion Flutter PDF Viewer'), +// actions: [ +// IconButton( +// icon: const Icon( +// Icons.bookmark, +// color: Colors.white, +// semanticLabel: 'Bookmark', +// ), +// onPressed: () { +// _pdfViewerKey.currentState?.openBookmarkView(); +// }, +// ), +// ], +// ), +// body: SfPdfViewer.asset( +// 'assets/pdfs/sample.pdf', +// key: _pdfViewerKey, +// ) +// ); +// } +// } +import 'package:flutter/services.dart'; + +final digitsOnlyFormatter = [FilteringTextInputFormatter.digitsOnly]; + +final decimalFormatter = [ + FilteringTextInputFormatter.allow(RegExp(r'^\d*\.?\d{0,2}$')), +]; + +class policyValidation extends ConsumerStatefulWidget { + final dynamic item; + const policyValidation({Key? key, this.item}) : super(key: key); + + @override + ConsumerState createState() => _policyValidationState(); +} + +class _policyValidationState extends ConsumerState { + // --- Api / state + late ApiService apiService; + bool isLoading = false; + dynamic managerId; + dynamic userId; + + String? pdfUrl; // full file path from API + + // --- Controllers (all editable fields) + late TextEditingController policyNumberController; + late TextEditingController insuredNameController; + late TextEditingController premiumController; + late TextEditingController remarksController; + + late TextEditingController rcNoController; + late TextEditingController issuedDateController; + late TextEditingController startDateController; + late TextEditingController endDateController; + + late TextEditingController tpController; + late TextEditingController odController; + late TextEditingController paController; + late TextEditingController cgstController; + late TextEditingController sgstController; + late TextEditingController igstController; + + late TextEditingController rtoStateCodeController; + late TextEditingController rtoCityCodeController; + late TextEditingController weightController; + late TextEditingController fuelTypeController; + late TextEditingController registrationDateController; + late TextEditingController yomController; + + late TextEditingController engineNoController; + late TextEditingController chassisNoController; + late TextEditingController makeController; + late TextEditingController modelController; + late TextEditingController cubicCapacityController; + late TextEditingController vehicleTypeController; + + late TextEditingController brokerNameController; + late TextEditingController commissionAmountController; + + final GlobalKey _pdfViewerKey = GlobalKey(); + final _formKey = GlobalKey(); + + @override + void initState() { + super.initState(); + apiService = ApiService(); + + // instantiate controllers with defaults + policyNumberController = TextEditingController(); + insuredNameController = TextEditingController(); + premiumController = TextEditingController(); + remarksController = TextEditingController(); + + rcNoController = TextEditingController(); + issuedDateController = TextEditingController(); + startDateController = TextEditingController(); + endDateController = TextEditingController(); + + tpController = TextEditingController(); + odController = TextEditingController(); + paController = TextEditingController(); + cgstController = TextEditingController(); + sgstController = TextEditingController(); + igstController = TextEditingController(); + + rtoStateCodeController = TextEditingController(); + rtoCityCodeController = TextEditingController(); + weightController = TextEditingController(); + fuelTypeController = TextEditingController(); + registrationDateController = TextEditingController(); + yomController = TextEditingController(); + + engineNoController = TextEditingController(); + chassisNoController = TextEditingController(); + makeController = TextEditingController(); + modelController = TextEditingController(); + cubicCapacityController = TextEditingController(); + vehicleTypeController = TextEditingController(); + + brokerNameController = TextEditingController(); + commissionAmountController = TextEditingController(); + + // Pre-fill controllers from initial `widget.item` (if available) + final item = widget.item as Map? ?? {}; + policyNumberController.text = item['policy_number']?.toString() ?? ''; + insuredNameController.text = item['insured_name']?.toString() ?? ''; + premiumController.text = item['premium_amount']?.toString() ?? ''; + remarksController.text = item['remarks']?.toString() ?? ''; + + rcNoController.text = item['rc_no']?.toString() ?? ''; + issuedDateController.text = item['issued_date']?.toString() ?? ''; + startDateController.text = item['start_date']?.toString() ?? ''; + endDateController.text = item['end_date']?.toString() ?? ''; + + tpController.text = item['tp']?.toString() ?? ''; + odController.text = item['od']?.toString() ?? ''; + paController.text = item['pa']?.toString() ?? ''; + cgstController.text = item['cgst']?.toString() ?? ''; + sgstController.text = item['sgst']?.toString() ?? ''; + igstController.text = item['igst']?.toString() ?? ''; + + rtoStateCodeController.text = item['rto_state_code']?.toString() ?? ''; + rtoCityCodeController.text = item['rto_city_code']?.toString() ?? ''; + weightController.text = item['weight']?.toString() ?? ''; + fuelTypeController.text = item['fuel_type']?.toString() ?? ''; + registrationDateController.text = + item['date_of_registration']?.toString() ?? ''; + yomController.text = item['year_of_manufacture']?.toString() ?? ''; + + engineNoController.text = item['engine_no']?.toString() ?? ''; + chassisNoController.text = item['chassis_no']?.toString() ?? ''; + makeController.text = item['make']?.toString() ?? ''; + modelController.text = item['model']?.toString() ?? ''; + cubicCapacityController.text = item['cubic_capacity']?.toString() ?? ''; + vehicleTypeController.text = item['vehicle_type']?.toString() ?? ''; + + brokerNameController.text = item['broker_name']?.toString() ?? ''; + commissionAmountController.text = + item['commission_amount']?.toString() ?? ''; + + // read providers + call APIs + Future.microtask(() { + try { + managerId = ref.read(managerIdProvider); + } catch (e) { + managerId = null; + } + try { + userId = ref.read(userIdProvider); + } catch (e) { + userId = null; + } + + getFindPolicy(); + getPolicyFilePath(); + }); + } + + @override + void dispose() { + policyNumberController.dispose(); + insuredNameController.dispose(); + premiumController.dispose(); + remarksController.dispose(); + + rcNoController.dispose(); + issuedDateController.dispose(); + startDateController.dispose(); + endDateController.dispose(); + + tpController.dispose(); + odController.dispose(); + paController.dispose(); + cgstController.dispose(); + sgstController.dispose(); + igstController.dispose(); + + rtoStateCodeController.dispose(); + rtoCityCodeController.dispose(); + weightController.dispose(); + fuelTypeController.dispose(); + registrationDateController.dispose(); + yomController.dispose(); + + engineNoController.dispose(); + chassisNoController.dispose(); + makeController.dispose(); + modelController.dispose(); + cubicCapacityController.dispose(); + vehicleTypeController.dispose(); + + brokerNameController.dispose(); + commissionAmountController.dispose(); + + super.dispose(); + } + + Future getPolicyFilePath() async { + _pdfViewerKey.currentState?.openBookmarkView(); + + final policyId = widget.item?['policy_id']; + if (policyId == null) { + debugPrint('policyValidation: policy id is null, skipping API call'); + return; + } + + if (!mounted) return; + setState(() => isLoading = true); + + try { + final url = Uri.parse( + '${Env.apiUrl}policy/PolicyFilePath?policy_id=$policyId&file_type=policy_pdf', + ); + + setState(() { + pdfUrl = "$url"; + }); + print("FINAL PDF URL -> $pdfUrl"); + } catch (e, st) { + print('Exception in getPolicyFilePath: $e\n$st'); + } finally { + if (!mounted) return; + setState(() => isLoading = false); + } + } + + Future getFindPolicy() async { + final policyId = widget.item?['policy_id']; + if (policyId == null) { + debugPrint('policyValidation: policy id is null, skipping API call'); + return; + } + + if (!mounted) return; + setState(() => isLoading = true); + + try { + final response = await apiService.findPolicyApi(policyId); + debugPrint('findPolicyApi response: $response'); + + if (response != null && response['status'] == 'success') { + final data = (response['data'] ?? {}) as Map; + + if (!mounted) return; + setState(() { + // update controllers (overwrite with authoritative API values) + policyNumberController.text = + data['policy_number']?.toString() ?? policyNumberController.text; + insuredNameController.text = + data['insured_name']?.toString() ?? insuredNameController.text; + premiumController.text = + data['premium_amount']?.toString() ?? premiumController.text; + remarksController.text = + data['remarks']?.toString() ?? remarksController.text; + + rcNoController.text = + data['rc_no']?.toString() ?? rcNoController.text; + issuedDateController.text = + data['issued_date']?.toString() ?? issuedDateController.text; + startDateController.text = + data['start_date']?.toString() ?? startDateController.text; + endDateController.text = + data['end_date']?.toString() ?? endDateController.text; + + tpController.text = data['tp']?.toString() ?? tpController.text; + odController.text = data['od']?.toString() ?? odController.text; + paController.text = data['pa']?.toString() ?? paController.text; + cgstController.text = data['cgst']?.toString() ?? cgstController.text; + sgstController.text = data['sgst']?.toString() ?? sgstController.text; + igstController.text = data['igst']?.toString() ?? igstController.text; + + rtoStateCodeController.text = + data['rto_state_code']?.toString() ?? rtoStateCodeController.text; + rtoCityCodeController.text = + data['rto_city_code']?.toString() ?? rtoCityCodeController.text; + weightController.text = + data['weight']?.toString() ?? weightController.text; + fuelTypeController.text = + data['fuel_type']?.toString() ?? fuelTypeController.text; + registrationDateController.text = + data['date_of_registration']?.toString() ?? + registrationDateController.text; + yomController.text = + data['year_of_manufacture']?.toString() ?? yomController.text; + + engineNoController.text = + data['engine_no']?.toString() ?? engineNoController.text; + chassisNoController.text = + data['chassis_no']?.toString() ?? chassisNoController.text; + makeController.text = data['make']?.toString() ?? makeController.text; + modelController.text = + data['model']?.toString() ?? modelController.text; + cubicCapacityController.text = + data['cubic_capacity']?.toString() ?? + cubicCapacityController.text; + vehicleTypeController.text = + data['vehicle_type']?.toString() ?? vehicleTypeController.text; + + brokerNameController.text = + data['broker_name']?.toString() ?? brokerNameController.text; + commissionAmountController.text = + data['commission_amount']?.toString() ?? + commissionAmountController.text; + }); + } else { + debugPrint( + 'findPolicyApi returned error: ${response?['message'] ?? 'unknown'}', + ); + } + } catch (e, st) { + debugPrint('Exception in getFindPolicy: $e\n$st'); + } finally { + if (!mounted) return; + setState(() => isLoading = false); + } + } + + Future _pickDate( + TextEditingController controller, + BuildContext context, + ) async { + DateTime initial = DateTime.now(); + try { + if (controller.text.isNotEmpty) { + // try parse dd-MM-yyyy + final parts = controller.text.split('-'); + if (parts.length == 3) { + final d = int.tryParse(parts[0]) ?? initial.day; + final m = int.tryParse(parts[1]) ?? initial.month; + final y = int.tryParse(parts[2]) ?? initial.year; + initial = DateTime(y, m, d); + } + } + } catch (_) {} + final picked = await showDatePicker( + context: context, + initialDate: initial, + firstDate: DateTime(1900), + lastDate: DateTime(2100), + ); + if (picked != null) { + controller.text = + "${picked.day.toString().padLeft(2, '0')}-${picked.month.toString().padLeft(2, '0')}-${picked.year}"; + } + } + + Future _save() async { + final policyId = widget.item?['policy_id']; + + if (policyId == null) { + ToastHelper.showWarningToast(context, 'Missing policy id'); + return; + } + + if (!_formKey.currentState!.validate()) { + ToastHelper.showWarningToast(context, "Please fill all required fields"); + return; + } + + if (widget.item?['is_data_accuracy_checked'] == "1") { + ToastHelper.showWarningToast(context, "Data already confirmed"); + return; + } + + final payload = { + "id": policyId, + "rc_no": rcNoController.text, + "insured_name": insuredNameController.text, + "issued_date": issuedDateController.text, + "start_date": startDateController.text, + "end_date": endDateController.text, + "premium_amount": premiumController.text, + "policy_number": policyNumberController.text, + "tp": tpController.text.isEmpty ? null : tpController.text, + "od": odController.text.isEmpty ? null : odController.text, + "pa": paController.text.isEmpty ? null : paController.text, + "cgst": cgstController.text.isEmpty ? null : cgstController.text, + "sgst": sgstController.text.isEmpty ? null : sgstController.text, + "igst": igstController.text.isEmpty ? null : igstController.text, + "rto_state_code": rtoStateCodeController.text.isEmpty + ? null + : rtoStateCodeController.text, + "rto_city_code": rtoCityCodeController.text.isEmpty + ? null + : rtoCityCodeController.text, + "weight": weightController.text.isEmpty ? null : weightController.text, + "fuel_type": fuelTypeController.text.isEmpty + ? null + : fuelTypeController.text, + "date_of_registration": registrationDateController.text.isEmpty + ? null + : registrationDateController.text, + "year_of_manufacture": yomController.text.isEmpty + ? null + : yomController.text, + "engine_no": engineNoController.text.isEmpty + ? null + : engineNoController.text, + "chassis_no": chassisNoController.text.isEmpty + ? null + : chassisNoController.text, + "make": makeController.text.isEmpty ? null : makeController.text, + "model": modelController.text.isEmpty ? null : modelController.text, + "cubic_capacity": cubicCapacityController.text.isEmpty + ? null + : cubicCapacityController.text, + "vehicle_type": vehicleTypeController.text.isEmpty + ? null + : vehicleTypeController.text, + "broker_name": brokerNameController.text.isEmpty + ? null + : brokerNameController.text, + "commission_amount": commissionAmountController.text.isEmpty + ? null + : commissionAmountController.text, + "updated_by": userId, + }; + + if (!mounted) return; + setState(() => isLoading = true); + + try { + debugPrint('FINAL SAVE PAYLOAD => $payload'); + final result = await apiService.updatePolicyApi(payload); + debugPrint('updatePolicy result => $result'); + + if (result != null && result['status'] == 'success') { + if (!mounted) return; + setState(() => isLoading = false); + context.pop(); + ToastHelper.showSuccessToast(context, "Policy updated successfully"); + + WidgetsBinding.instance.addPostFrameCallback((_) { + // Now safe to do navigation + provider updates + final container = ProviderScope.containerOf(context); + container.read(policyDataAcurancyRefreshProvider.notifier).state = + true; + }); + } else { + if (!mounted) return; + setState(() => isLoading = false); + ToastHelper.showErrorToast( + context, + result?['message']?.toString() ?? 'Error updating policy', + ); + } + } catch (e, st) { + debugPrint('Exception in updatePolicy: $e\n$st'); + if (!mounted) return; + setState(() => isLoading = false); + ToastHelper.showWarningToast(context, 'Exception: $e'); + } + } + + Future _fetchCommision() async { + final policyId = widget.item?['policy_id']; + + if (policyId == null) { + ToastHelper.showWarningToast(context, 'Missing policy id'); + return; + } + + if (!_formKey.currentState!.validate()) { + ToastHelper.showWarningToast(context, "Please fill all required fields"); + return; + } + + // if (widget.item?['is_data_accuracy_checked'] == "1") { + // ToastHelper.showWarningToast(context, "Data already confirmed"); + // return; + // } + + final payload = { + "id": policyId, + "rc_no": rcNoController.text, + "insured_name": insuredNameController.text, + "issued_date": issuedDateController.text, + "start_date": startDateController.text, + "end_date": endDateController.text, + "premium_amount": premiumController.text, + "policy_number": policyNumberController.text, + "tp": tpController.text.isEmpty ? null : tpController.text, + "od": odController.text.isEmpty ? null : odController.text, + "pa": paController.text.isEmpty ? null : paController.text, + "cgst": cgstController.text.isEmpty ? null : cgstController.text, + "sgst": sgstController.text.isEmpty ? null : sgstController.text, + "igst": igstController.text.isEmpty ? null : igstController.text, + "rto_state_code": rtoStateCodeController.text.isEmpty + ? null + : rtoStateCodeController.text, + "rto_city_code": rtoCityCodeController.text.isEmpty + ? null + : rtoCityCodeController.text, + "weight": weightController.text.isEmpty ? null : weightController.text, + "fuel_type": fuelTypeController.text.isEmpty + ? null + : fuelTypeController.text, + "date_of_registration": registrationDateController.text.isEmpty + ? null + : registrationDateController.text, + "year_of_manufacture": yomController.text.isEmpty + ? null + : yomController.text, + "engine_no": engineNoController.text.isEmpty + ? null + : engineNoController.text, + "chassis_no": chassisNoController.text.isEmpty + ? null + : chassisNoController.text, + "make": makeController.text.isEmpty ? null : makeController.text, + "model": modelController.text.isEmpty ? null : modelController.text, + "cubic_capacity": cubicCapacityController.text.isEmpty + ? null + : cubicCapacityController.text, + "vehicle_type": vehicleTypeController.text.isEmpty + ? null + : vehicleTypeController.text, + "broker_name": brokerNameController.text.isEmpty + ? null + : brokerNameController.text, + // "commission_amount": commissionAmountController.text.isEmpty + // ? null + // : commissionAmountController.text, + "updated_by": userId, + // "insurance_plan_type": + }; + + if (!mounted) return; + setState(() => isLoading = true); + + try { + debugPrint('FINAL SAVE PAYLOAD => $payload'); + final result = await apiService.calculateCommissionRequest(payload); + debugPrint('updatePolicy result => $result'); + + if (result != null && result['status'] == 'success') { + if (!mounted) return; + setState(() => isLoading = false); + // context.pop(); + ToastHelper.showSuccessToast(context, "Policy updated successfully"); + + // WidgetsBinding.instance.addPostFrameCallback((_) { + // // Now safe to do navigation + provider updates + // final container = ProviderScope.containerOf(context); + // container.read(policyDataAcurancyRefreshProvider.notifier).state = + // true; + // }); + } else { + if (!mounted) return; + setState(() => isLoading = false); + ToastHelper.showErrorToast( + context, + result?['message']?.toString() ?? 'Error updating policy', + ); + } + } catch (e, st) { + debugPrint('Exception in updatePolicy: $e\n$st'); + if (!mounted) return; + setState(() => isLoading = false); + ToastHelper.showWarningToast(context, 'Exception: $e'); + } + } + + Widget _sectionHeader(String title, {IconData icon = Icons.info}) { + return Container( + padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 12), + decoration: BoxDecoration( + color: Color(0xFFE3F9F8), + borderRadius: BorderRadius.circular(6), + ), + child: Row( + children: [ + Container( + padding: const EdgeInsets.all(6), + decoration: const BoxDecoration( + color: Color(0xFF2E7D6E), + shape: BoxShape.circle, + ), + child: Icon(icon, size: 16, color: Colors.white), + ), + const SizedBox(width: 10), + Text( + title, + style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w600), + ), + ], + ), + ); + } + + Widget _buildInput( + String label, + TextEditingController controller, { + TextInputType keyboardType = TextInputType.text, + bool readOnly = false, + VoidCallback? onTap, + bool required = false, + List? inputFormatters, // 👈 ADD THIS + }) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(label, style: const TextStyle(fontWeight: FontWeight.w500)), + const SizedBox(height: 6), + TextFormField( + controller: controller, + keyboardType: keyboardType, + readOnly: readOnly, + onTap: onTap, + inputFormatters: inputFormatters, // 👈 USE HERE + validator: (value) { + if (required && (value == null || value.trim().isEmpty)) { + return "$label is required"; + } + return null; + }, + decoration: InputDecoration( + border: OutlineInputBorder(borderRadius: BorderRadius.circular(6)), + isDense: true, + + // alignment fix + helperText: ' ', + helperStyle: const TextStyle(height: 1), + errorMaxLines: 2, + + errorBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(6), + borderSide: const BorderSide(color: Colors.red), + ), + focusedErrorBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(6), + borderSide: const BorderSide(color: Colors.red), + ), + contentPadding: const EdgeInsets.all(10), + ), + ), + ], + ); + } + + @override + Widget build(BuildContext context) { + // Ensure global AppHeader remains visible by pushing content down by 64px + return SafeArea( + child: Scaffold( + backgroundColor: Color(0xffD9EBE8), + // ⭐ FIXED BUTTON FOOTER + bottomNavigationBar: Container( + padding: const EdgeInsets.all(16), + color: const Color(0xFFF7FBFA), + child: Row( + mainAxisAlignment: MainAxisAlignment.end, // 👉 move button to right + children: [ + ElevatedButton( + onPressed: _fetchCommision, + style: ElevatedButton.styleFrom( + backgroundColor: const Color(0xFF2E7D6E), + foregroundColor: Colors.white, + padding: const EdgeInsets.symmetric( + vertical: 10, + horizontal: 20, // 👉 smaller width + ), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + ), + ), + child: const Text( + 'Fetch Commission', + style: TextStyle(fontSize: 14, fontWeight: FontWeight.w600), + ), + ), + SizedBox(width: 10), + ElevatedButton( + onPressed: widget.item?['is_data_accuracy_checked'] == '1' + ? null + : _save, + style: ElevatedButton.styleFrom( + backgroundColor: const Color(0xFF2E7D6E), + foregroundColor: Colors.white, + padding: const EdgeInsets.symmetric( + vertical: 10, + horizontal: 20, // 👉 smaller width + ), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + ), + ), + child: const Text( + 'Confirm Data Accuracy', + style: TextStyle(fontSize: 14, fontWeight: FontWeight.w600), + ), + ), + ], + ), + ), + body: Container( + // margin: const EdgeInsets.only(top: 64), // <-- keeps popup under global AppHeader + width: double.infinity, + // height: MediaQuery.of(context).size.height - 64, + color: const Color(0xFFF7FBFA), + child: Column( + children: [ + // In-body header + Container( + height: 56, + color: Color(0xFFE3F9F8), + padding: const EdgeInsets.symmetric(horizontal: 12), + child: Row( + children: [ + IconButton( + icon: const Icon(Icons.file_open), + onPressed: () => context.pop(), + ), + const Expanded( + child: Text( + 'Insurance Policy Review', + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.w600, + ), + ), + ), + if (isLoading) + const SizedBox( + width: 12, + height: 12, + child: CircularProgressIndicator(strokeWidth: 2), + ), + const SizedBox(width: 8), + IconButton( + icon: const Icon(Icons.close), + onPressed: () => context.pop(), + ), + ], + ), + ), + + // Body: left PDF preview + right grouped form + Expanded( + child: Padding( + padding: const EdgeInsets.all(16.0), + child: Row( + children: [ + // LEFT - PDF placeholder + Expanded( + flex: 1, + child: Container( + margin: const EdgeInsets.only(right: 12), + padding: const EdgeInsets.all(12), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(8), + border: Border.all(color: Colors.grey.shade200), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + const Text( + 'Original Policy Document', + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w600, + ), + ), + const SizedBox(height: 8), + + Expanded( + child: FutureBuilder( + future: loadNetworkPdfBytes(pdfUrl!), + builder: (context, snapshot) { + if (!snapshot.hasData) { + return const Center( + child: CircularProgressIndicator(), + ); + } + + final pdfBytes = snapshot.data!; + + return PdfDocumentLoader.openData( + pdfBytes, + documentBuilder: + (context, pdfDocument, pageCount) { + return ListView.builder( + itemCount: pageCount, + itemBuilder: (context, index) { + return Container( + color: Colors.black12, + child: PdfPageView( + pdfDocument: pdfDocument, + pageNumber: index + 1, + ), + ); + }, + ); + }, + ); + }, + ), + ), + + // Expanded( + // child: PdfDocumentLoader.openAsset( + // 'assets/pdfs/sample.pdf', + // documentBuilder: (context, pdfDocument, pageCount) => LayoutBuilder( + // builder: (context, constraints) => ListView.builder( + // itemCount: pageCount, + // itemBuilder: (context, index) => Container( + // // margin: EdgeInsets.all(margin), + // // padding: EdgeInsets.all(padding), + // color: Colors.black12, + // child: PdfPageView( + // pdfDocument: pdfDocument, + // pageNumber: index + 1, + // ) + // ) + // ) + // ), + // ) + // ) + ], + ), + ), + ), + // RIGHT - Grouped Form + Expanded( + flex: 1, + child: SingleChildScrollView( + child: Form( + key: _formKey, + autovalidateMode: + AutovalidateMode.onUserInteraction, + child: Card( + elevation: 2, + color: Colors.white, + margin: EdgeInsets.zero, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + ), + child: Padding( + padding: const EdgeInsets.all(16), + child: Column( + crossAxisAlignment: + CrossAxisAlignment.stretch, + children: [ + // Policy Details + Card( + elevation: 5, + color: Colors.white, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + ), + child: Padding( + padding: const EdgeInsets.all(12.0), + child: Column( + crossAxisAlignment: + CrossAxisAlignment.stretch, + children: [ + _sectionHeader( + 'Policy Details', + icon: Icons.policy, + ), + SizedBox(height: 10), + Row( + children: [ + Expanded( + child: _buildInput( + 'Policy Number', + policyNumberController, + required: true, + ), + ), + const SizedBox(width: 8), + Expanded( + child: _buildInput( + 'Insured Name', + insuredNameController, + required: true, + ), + ), + ], + ), + Row( + children: [ + Expanded( + child: _buildInput( + 'Issued Date', + issuedDateController, + readOnly: true, + required: true, + onTap: () => _pickDate( + issuedDateController, + context, + ), + ), + ), + const SizedBox(width: 8), + Expanded( + child: _buildInput( + 'Start Date', + startDateController, + readOnly: true, + required: true, + onTap: () => _pickDate( + startDateController, + context, + ), + ), + ), + const SizedBox(width: 8), + Expanded( + child: _buildInput( + 'End Date', + endDateController, + readOnly: true, + required: true, + onTap: () => _pickDate( + endDateController, + context, + ), + ), + ), + ], + ), + Row( + children: [ + Expanded( + child: _buildInput( + 'Premium Amount', + premiumController, + required: true, + keyboardType: + TextInputType.numberWithOptions( + decimal: true, + ), + inputFormatters: + decimalFormatter, + ), + ), + const SizedBox(width: 8), + Expanded( + child: _buildInput( + 'Remarks', + remarksController, + required: false, + ), + ), + ], + ), + ], + ), + ), + ), + + const SizedBox(height: 12), + + // Tax Details + Card( + elevation: 5, + color: Colors.white, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + ), + child: Padding( + padding: const EdgeInsets.all(12.0), + child: Column( + crossAxisAlignment: + CrossAxisAlignment.stretch, + children: [ + _sectionHeader( + 'Tax / Cover Details', + icon: Icons.attach_money, + ), + SizedBox(height: 10), + Row( + children: [ + Expanded( + child: _buildInput( + 'TP', + tpController, + required: true, + keyboardType: + TextInputType.numberWithOptions( + decimal: true, + ), + inputFormatters: + decimalFormatter, + ), + ), + const SizedBox(width: 8), + Expanded( + child: _buildInput( + 'OD', + odController, + required: true, + keyboardType: + TextInputType.numberWithOptions( + decimal: true, + ), + inputFormatters: + decimalFormatter, + ), + ), + const SizedBox(width: 8), + Expanded( + child: _buildInput( + 'PA', + paController, + required: true, + keyboardType: + TextInputType.numberWithOptions( + decimal: true, + ), + inputFormatters: + decimalFormatter, + ), + ), + ], + ), + Row( + children: [ + Expanded( + child: _buildInput( + 'CGST', + cgstController, + required: true, + keyboardType: + TextInputType.numberWithOptions( + decimal: true, + ), + inputFormatters: + decimalFormatter, + ), + ), + const SizedBox(width: 8), + Expanded( + child: _buildInput( + 'SGST', + sgstController, + required: true, + keyboardType: + TextInputType.numberWithOptions( + decimal: true, + ), + inputFormatters: + decimalFormatter, + ), + ), + const SizedBox(width: 8), + Expanded( + child: _buildInput( + 'IGST', + igstController, + required: true, + keyboardType: + TextInputType.numberWithOptions( + decimal: true, + ), + inputFormatters: + decimalFormatter, + ), + ), + ], + ), + ], + ), + ), + ), + + const SizedBox(height: 12), + + // Vehicle Details + Card( + elevation: 5, + color: Colors.white, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + ), + child: Padding( + padding: const EdgeInsets.all(12.0), + child: Column( + crossAxisAlignment: + CrossAxisAlignment.stretch, + children: [ + _sectionHeader( + 'Vehicle Details', + icon: Icons.directions_car, + ), + SizedBox(height: 10), + Row( + children: [ + Expanded( + child: _buildInput( + 'RTO State Name', + rtoStateCodeController, + required: false, + ), + ), + const SizedBox(width: 8), + Expanded( + child: _buildInput( + 'RTO City Code', + rtoCityCodeController, + required: false, + keyboardType: + TextInputType.number, + inputFormatters: + digitsOnlyFormatter, + ), + ), + const SizedBox(width: 8), + Expanded( + child: _buildInput( + 'Weight', + weightController, + required: false, + keyboardType: + TextInputType.number, + inputFormatters: + digitsOnlyFormatter, + ), + ), + ], + ), + Row( + children: [ + Expanded( + child: _buildInput( + 'Fuel Type', + fuelTypeController, + required: false, + ), + ), + const SizedBox(width: 8), + Expanded( + child: _buildInput( + 'Date of Registration', + registrationDateController, + required: false, + readOnly: true, + onTap: () => _pickDate( + registrationDateController, + context, + ), + ), + ), + const SizedBox(width: 8), + Expanded( + child: _buildInput( + 'Year of Manufacture', + yomController, + required: false, + keyboardType: + TextInputType.number, + inputFormatters: + digitsOnlyFormatter, + ), + ), + ], + ), + Row( + children: [ + Expanded( + child: _buildInput( + 'Engine No', + engineNoController, + required: false, + ), + ), + const SizedBox(width: 8), + Expanded( + child: _buildInput( + 'Chassis No', + chassisNoController, + required: false, + ), + ), + const SizedBox(width: 8), + Expanded( + child: Expanded( + child: _buildInput( + 'Make', + makeController, + required: false, + ), + ), + ), + ], + ), + Row( + children: [ + Expanded( + child: _buildInput( + 'Model', + modelController, + required: false, + ), + ), + const SizedBox(width: 8), + Expanded( + child: _buildInput( + 'Cubic Capacity', + cubicCapacityController, + required: false, + keyboardType: + TextInputType.numberWithOptions( + decimal: true, + ), + inputFormatters: + decimalFormatter, + ), + ), + const SizedBox(width: 8), + Expanded( + child: _buildInput( + 'Vehicle Type', + vehicleTypeController, + required: false, + ), + ), + ], + ), + ], + ), + ), + ), + + const SizedBox(height: 12), + + // Broker Details + Card( + elevation: 5, + color: Colors.white, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + ), + child: Padding( + padding: const EdgeInsets.all(12.0), + child: Column( + crossAxisAlignment: + CrossAxisAlignment.stretch, + children: [ + _sectionHeader( + 'Broker / Commission', + icon: Icons.person, + ), + SizedBox(height: 10), + Row( + children: [ + Expanded( + child: _buildInput( + 'Broker Name', + brokerNameController, + required: false, + ), + ), + const SizedBox(width: 8), + Expanded( + child: _buildInput( + 'Commission Amount', + commissionAmountController, + required: false, + keyboardType: + TextInputType.numberWithOptions( + decimal: true, + ), + inputFormatters: + decimalFormatter, + ), + ), + ], + ), + ], + ), + ), + ), + + const SizedBox( + height: 20, + ), // spacing above fixed button + ], + ), + ), + ), + ), + ), + ), + ], + ), + ), + ), + ], + ), + ), + ), + ); + } +} diff --git a/lib/presentation/themes/charts/barChart.dart b/lib/presentation/themes/charts/barChart.dart index 168de45..8ca5b80 100644 --- a/lib/presentation/themes/charts/barChart.dart +++ b/lib/presentation/themes/charts/barChart.dart @@ -1,13 +1,18 @@ import 'package:fl_chart/fl_chart.dart'; import 'package:flutter/cupertino.dart'; 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/services/api_service.dart'; import '../../../data/models/bar_data.dart'; +import '../../providers/manager_provider.dart'; import 'appChartColors.dart'; -class CustomBarChart extends StatefulWidget { +class CustomBarChart extends ConsumerStatefulWidget { final List dataList; final List labels; + final List? shortName; final String title; final String? btnName; final double maxY; @@ -17,6 +22,7 @@ class CustomBarChart extends StatefulWidget { super.key, required this.dataList, required this.labels, + this.shortName, this.btnName, this.title = "Horizontal Bar Chart", this.maxY = 20, @@ -26,23 +32,27 @@ class CustomBarChart extends StatefulWidget { final shadowColor = const Color(0xFFCCCCCC); @override - State createState() => _CustomBarChartState(); + // State createState() => _CustomBarChartState(); + ConsumerState createState() => _CustomBarChartState(); } -class _CustomBarChartState extends State { +class _CustomBarChartState extends ConsumerState { int touchedGroupIndex = -1; late int rotationTurns; dynamic managerId; + late ApiService apiService; @override void initState() { super.initState(); + apiService = ApiService(); rotationTurns = widget.initialRotation; - // Future.microtask(() { - // managerId = ref.read(managerIdProvider); - // }); + Future.microtask(() { + managerId = ref.read(managerIdProvider); + }); } + final indianFormatter = NumberFormat('#,##,##0', 'en_IN'); BarChartGroupData generateBarGroup( int x, Color color, @@ -53,180 +63,267 @@ class _CustomBarChartState extends State { return BarChartGroupData( x: x, groupVertically: false, + // showingTooltipIndicators: [0], // barsSpace: 30, barRods: [ - BarChartRodData(toY: value1, color: Colors.cyan.shade300, width: 4), + BarChartRodData(toY: 0, color: Colors.transparent, width: 10), + BarChartRodData( + toY: value1, + color: Colors.cyan.shade300, + width: 20, + borderRadius: BorderRadius.circular(0), + ), // BarChartRodData(toY: 2, color: Colors.orange.shade300, width: 6), - BarChartRodData(toY: value2, color: Colors.orange.shade300, width: 4), + BarChartRodData( + toY: value2, + color: Colors.orange.shade300, + width: 20, + borderRadius: BorderRadius.circular(0), + ), ], showingTooltipIndicators: touchedGroupIndex == x ? [0] : [], ); } + @override + // Widget build(BuildContext context) { + // return AspectRatio( + // aspectRatio: 1.4, + // + // ); + // } @override Widget build(BuildContext context) { - return AspectRatio( - aspectRatio: 1.4, - child: BarChart( - BarChartData( - alignment: BarChartAlignment.spaceBetween, - // verticalAxis: Axis.horizontal, - rotationQuarterTurns: rotationTurns, + return LayoutBuilder( + builder: (context, constraints) { + return SizedBox( + width: constraints.maxWidth, + height: constraints.maxHeight, + child: BarChart( + BarChartData( + // alignment: BarChartAlignment.spaceBetween, + alignment: BarChartAlignment.start, + // verticalAxis: Axis.horizontal, + // rotationQuarterTurns: rotationTurns, + barTouchData: BarTouchData( + enabled: true, + handleBuiltInTouches: true, + touchTooltipData: BarTouchTooltipData( + tooltipMargin: 6, + tooltipRoundedRadius: 6, + tooltipPadding: const EdgeInsets.symmetric( + horizontal: 10, + vertical: 6, + ), + // ask fl_chart to fit tooltip inside available space + fitInsideVertically: + true, // if your fl_chart version supports it + fitInsideHorizontally: + true, // if your fl_chart version supports it + getTooltipItem: + ( + BarChartGroupData group, + int groupIndex, + BarChartRodData rod, + int rodIndex, + ) { + final textColor = rodIndex == 1 + ? Colors.cyan.shade300 + : Colors.orange.shade300; + final textStyle = TextStyle( + color: textColor, + fontSize: 12, + fontWeight: FontWeight.w600, + ); - barTouchData: BarTouchData( - enabled: true, - handleBuiltInTouches: true, - touchTooltipData: BarTouchTooltipData( - tooltipMargin: 6, - tooltipRoundedRadius: 6, - tooltipPadding: const EdgeInsets.symmetric( - horizontal: 10, - vertical: 6, - ), - // ask fl_chart to fit tooltip inside available space - fitInsideVertically: true, // if your fl_chart version supports it - fitInsideHorizontally: - true, // if your fl_chart version supports it - getTooltipItem: - ( - BarChartGroupData group, - int groupIndex, - BarChartRodData rod, - int rodIndex, - ) { - final textColor = rodIndex == 0 - ? Colors.cyan.shade300 - : Colors.orange.shade300; - final textStyle = TextStyle( - color: textColor, - fontSize: 12, - fontWeight: FontWeight.w600, + final formattedValue = indianFormatter.format( + rod.toY.round(), + ); + + return BarTooltipItem(formattedValue, textStyle); + // return BarTooltipItem('${rod.toY.round()}', textStyle); + }, + ), + + touchCallback: (FlTouchEvent e, BarTouchResponse? r) async { + // optional: change state to highlight tapped group + if (r == null || r.spot == null) { + setState(() => touchedGroupIndex = -1); + return; + } + setState( + () => touchedGroupIndex = r.spot!.touchedBarGroupIndex, + ); + + final spot = r.spot!; + final groupIndex = spot.touchedBarGroupIndex; + final rodIndex = spot.touchedRodDataIndex; + + final barData = widget.dataList[groupIndex]; + + final name = barData.chartName; + final val = barData.id; + final month = rodIndex == 1 ? 'current' : 'previous'; + print('name - $name'); + print('val - $val'); + print('month - $month'); + print('managerId - $managerId'); + if (e is FlTapUpEvent) { + debugPrint( + 'Tapped value → ${rodIndex == 1 ? barData.value1 : barData.value2} -> ${barData.id}-' + ' ${rodIndex == 1 ? 'current' : 'previous'} -> ${barData.chartName}', ); - return BarTooltipItem('${rod.toY.round()}', textStyle); - }, - ), - - touchCallback: (FlTouchEvent e, BarTouchResponse? r) { - // optional: change state to highlight tapped group - if (r == null || r.spot == null) { - setState(() => touchedGroupIndex = -1); - return; - } - setState(() => touchedGroupIndex = r.spot!.touchedBarGroupIndex); - - final spot = r.spot!; - final groupIndex = spot.touchedBarGroupIndex; - final rodIndex = spot.touchedRodDataIndex; - - final barData = widget.dataList[groupIndex]; - - if (e is FlTapUpEvent) { - debugPrint( - 'Tapped value → ${rodIndex == 0 ? barData.value1 : barData.value2} -> ${barData.id}-' - ' ${rodIndex == 0 ? 'current' : 'previous'} -> ${barData.chartName}', - ); - // await apiService.generateExcel() - } - }, - ), - - borderData: FlBorderData( - show: true, - border: Border( - bottom: BorderSide( - color: AppColors.contentColorBlack, - width: 0.1, - ), - right: BorderSide(color: AppColors.contentColorBlack, width: 0.1), - ), - ), - gridData: FlGridData( - show: true, - // drawVerticalLine: true, - drawVerticalLine: false, - getDrawingHorizontalLine: (value) => FlLine( - // color: AppColors.gridLinesColor.withValues(alpha: 0.2), - color: Colors.blueGrey.shade100, - // color: Colors.black, - // color: AppColors.borderColor, - strokeWidth: 0.3, - ), - ), - titlesData: FlTitlesData( - bottomTitles: AxisTitles( - axisNameWidget: widget.btnName != null - ? Text(widget.btnName!) - : Text(''), - axisNameSize: 10, - sideTitles: SideTitles( - reservedSize: 60, - showTitles: true, - getTitlesWidget: (value, meta) { - final i = value.toInt(); - if (i >= widget.labels.length) return const SizedBox(); - return SideTitleWidget( - meta: meta, - child: Text( - widget.labels[i], - style: GoogleFonts.poppins( - fontSize: 9.5, - fontWeight: FontWeight.w500, - ), - textAlign: TextAlign.end, - ), - ); + if (managerId != null) { + print('managerId - $managerId'); + await apiService.generateChartExcel( + name, + val, + month, + managerId, + ); + } + } }, ), - ), - rightTitles: AxisTitles( - sideTitles: SideTitles( - showTitles: true, - getTitlesWidget: (value, meta) { - return Transform.rotate( - angle: -3, - child: Padding( - padding: const EdgeInsets.only(right: 3.0), - child: Text( - value.toInt().toString(), - textAlign: TextAlign.end, - style: GoogleFonts.inter( - fontSize: 9, - fontWeight: FontWeight.w400, + borderData: FlBorderData( + show: true, + border: Border( + bottom: BorderSide( + color: AppColors.contentColorBlack, + width: 0.1, + ), + left: BorderSide( + color: AppColors.contentColorBlack, + width: 0.1, + ), + ), + ), + gridData: FlGridData( + show: true, + // drawVerticalLine: true, + drawVerticalLine: false, + getDrawingHorizontalLine: (value) => FlLine( + // color: AppColors.gridLinesColor.withValues(alpha: 0.2), + color: Colors.blueGrey.shade100, + // color: Colors.black, + // color: AppColors.borderColor, + strokeWidth: 0.3, + ), + ), + titlesData: FlTitlesData( + bottomTitles: AxisTitles( + axisNameWidget: widget.btnName != null + ? Text(widget.btnName!) + : Text(''), + axisNameSize: 10, + sideTitles: SideTitles( + reservedSize: 30, + showTitles: true, + getTitlesWidget: (value, meta) { + final i = value.toInt(); + if (i >= widget.labels.length) return const SizedBox(); + final hasShortName = + widget.shortName != null && + widget.shortName!.length > i && + widget.shortName![i].trim().isNotEmpty; + + return SideTitleWidget( + meta: meta, + child: hasShortName + ? Tooltip( + message: widget.shortName![i], + child: Text( + widget.labels[i], + style: GoogleFonts.poppins( + fontSize: 9.5, + fontWeight: FontWeight.w500, + ), + textAlign: TextAlign.end, + ), + ) + : Text( + widget.labels[i], + style: GoogleFonts.poppins( + fontSize: 9.5, + fontWeight: FontWeight.w500, + ), + textAlign: TextAlign.end, + ), + ); + }, + ), + ), + + leftTitles: AxisTitles( + sideTitles: SideTitles( + showTitles: true, + // interval: 10, + getTitlesWidget: (value, meta) { + // print("valuevalue - $value"); + + final text = value.toInt().toString(); + + final bool isLast = value == meta.max; + // if (isLast) { + // return const SizedBox.shrink(); // 👈 hide last label + // } + // print('value: $value'); + // print('text: $text'); + // print('length: ${text.length}'); + // print('isLast: $isLast'); + return Transform.rotate( + // angle: -3, + angle: 0, + child: Padding( + padding: const EdgeInsets.only(right: 3.0), + child: Text( + isLast ? '0' : value.toInt().toString(), + textAlign: TextAlign.end, + style: GoogleFonts.inter( + fontSize: 9, + color: isLast ? Colors.white : Colors.black, + fontWeight: isLast + ? FontWeight.w100 + : FontWeight.w400, + ), + ), ), - ), - ), - ); - }, - reservedSize: 45, + ); + }, + reservedSize: 45, + ), + ), + rightTitles: const AxisTitles(), + topTitles: const AxisTitles(), ), - ), - leftTitles: const AxisTitles(), - topTitles: const AxisTitles(), - ), - barGroups: widget.dataList.asMap().entries.map((e) { - final index = e.key; - final data = e.value; - print('generateBarGroupBAR - $data'); - return generateBarGroup( - index, - data.color, - data.value1, - data.value2, - data.shadowValue, - ); - }).toList(), + barGroups: widget.dataList.asMap().entries.map((e) { + final index = e.key; + final data = e.value; + print('generateBarGroupBAR - $data'); + return generateBarGroup( + index, + data.color, + data.value1, + data.value2, + data.shadowValue, + ); + }).toList(), - // maxY: widget.maxY, - maxY: (widget.dataList != null && widget.dataList!.isNotEmpty) - ? widget.dataList! - .map((e) => e.value1 > e.value2 ? e.value1 : e.value2) - .reduce((a, b) => a > b ? a : b) + - 15 - : 20, - ), - ), + // maxY: widget.maxY, + maxY: (widget.dataList != null && widget.dataList!.isNotEmpty) + ? widget.dataList! + .map( + (e) => e.value1 > e.value2 ? e.value1 : e.value2, + ) + .reduce((a, b) => a > b ? a : b) + + 15 + : 20, + ), + ), + ); + }, ); } } diff --git a/lib/presentation/themes/charts/stackedBarChart.dart b/lib/presentation/themes/charts/stackedBarChart.dart index 9af08ef..ce3cf94 100644 --- a/lib/presentation/themes/charts/stackedBarChart.dart +++ b/lib/presentation/themes/charts/stackedBarChart.dart @@ -1,8 +1,10 @@ // custom_stacked_bar_chart.dart import 'package:fl_chart/fl_chart.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:google_fonts/google_fonts.dart'; +import '../../../core/services/api_service.dart'; import '../../../data/models/bar_data.dart'; import 'package:fl_chart/fl_chart.dart'; @@ -13,10 +15,12 @@ import 'package:fl_chart/fl_chart.dart'; import 'package:flutter/material.dart'; import 'package:google_fonts/google_fonts.dart'; +import '../../providers/manager_provider.dart'; import 'appChartColors.dart'; -class CustomStackedBarChart extends StatefulWidget { +class CustomStackedBarChart extends ConsumerStatefulWidget { final List labels; + final List labelsId; final List dataList; final double maxY; final int initialRotation; @@ -27,6 +31,7 @@ class CustomStackedBarChart extends StatefulWidget { const CustomStackedBarChart({ super.key, required this.labels, + required this.labelsId, required this.dataList, this.maxY = 100.0, this.initialRotation = 1, @@ -36,16 +41,23 @@ class CustomStackedBarChart extends StatefulWidget { }); @override - State createState() => _CustomStackedBarChartState(); + ConsumerState createState() => + _CustomStackedBarChartState(); } -class _CustomStackedBarChartState extends State { +class _CustomStackedBarChartState extends ConsumerState { late int rotationTurns; + dynamic managerId; + late ApiService apiService; @override void initState() { super.initState(); + apiService = ApiService(); rotationTurns = widget.initialRotation; + Future.microtask(() { + managerId = ref.read(managerIdProvider); + }); } @override @@ -191,6 +203,37 @@ class _CustomStackedBarChartState extends State { ); }, ), + + touchCallback: (event, response) async { + if (response == null || response.spot == null) return; + + // ✅ ONLY handle clicks + if (event is! FlTapUpEvent) return; + + final spot = response.spot!; + final int groupIndex = spot.touchedBarGroupIndex; + final int rodIndex = spot.touchedRodDataIndex; + final int stackIndex = spot.touchedStackItemIndex!; + + final label = + widget.vehicleTypeNames?[groupIndex][stackIndex] ?? 'Unknown'; + final isCurrentMonth = rodIndex == 0; + final monthLabel = isCurrentMonth ? 'Current' : 'Previous'; + final categoryLabel = widget.labels[groupIndex]; + final categoryLabelID = widget.labelsId[groupIndex]; + + debugPrint( + 'Clicked stack → $label -> $monthLabel ->$managerId ->$categoryLabel->$categoryLabelID', + ); + + await apiService.generatePerformanceDashboardExcel( + 'StaffProduct', + label, + categoryLabelID, + monthLabel, + managerId, + ); + }, ), barGroups: widget.productsList.asMap().entries.map((entry) { diff --git a/lib/presentation/themes/indicators/text_field_theme.dart b/lib/presentation/themes/indicators/text_field_theme.dart index 106e4c2..43eb001 100644 --- a/lib/presentation/themes/indicators/text_field_theme.dart +++ b/lib/presentation/themes/indicators/text_field_theme.dart @@ -34,6 +34,9 @@ class ThemedFormField extends HookWidget { this.horizonalPad, this.enableBorderWidth, this.textColr, + + this.errFieldFont, + this.errFieldHgt, }); final double? verticalPad; @@ -53,6 +56,8 @@ class ThemedFormField extends HookWidget { final Color? borderColor; final Color? errorBorderColor; final Color? errorTextColor; + final double? errFieldFont; + final double? errFieldHgt; final double? txtwidth; final bool readOnly; final bool isdense; @@ -91,7 +96,13 @@ class ThemedFormField extends HookWidget { ); final inputDecoration = InputDecoration( isDense: isdense, - errorStyle: TextStyle(color: Color(0xFFD83731)), + + // errorStyle: TextStyle(color: Color(0xFFD83731)), + // errorStyle: TextStyle( + // color: Color(0xFFD83731), + // fontSize: errFieldFont ?? 12, + // height: errFieldHgt ?? 10, + // ), errorBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(10), borderSide: BorderSide( @@ -140,7 +151,7 @@ class ThemedFormField extends HookWidget { borderSide: BorderSide(color: borderColor ?? Color(0xFFFFFFFF)), ) : OutlineInputBorder( - borderRadius: BorderRadius.circular(10), + borderRadius: BorderRadius.circular(bordedRad ?? 10), borderSide: BorderSide( color: highlightColor ?? borderColor ?? Color(0xFF50A398), width: 2, diff --git a/lib/presentation/themes/indicators/text_field_theme_inline_editor.dart b/lib/presentation/themes/indicators/text_field_theme_inline_editor.dart index 1b3ebe9..830ad51 100644 --- a/lib/presentation/themes/indicators/text_field_theme_inline_editor.dart +++ b/lib/presentation/themes/indicators/text_field_theme_inline_editor.dart @@ -182,6 +182,7 @@ class ThemedFormInlineField extends HookWidget { suffixIcon: isObscurable ? obscureBtn : null, ); return Container( + width: txtwidth ?? MediaQuery.of(context).size.width, height: txtheight ?? null, child: DecoratedBox( decoration: boxDecoration, diff --git a/lib/presentation/widgets/topbar.dart b/lib/presentation/widgets/topbar.dart index c2db9f2..395267c 100644 --- a/lib/presentation/widgets/topbar.dart +++ b/lib/presentation/widgets/topbar.dart @@ -11,6 +11,8 @@ import '../../core/routing/routes.dart'; import '../layouts/responsive_layout.dart'; import '../providers/manager_provider.dart'; import '../providers/userRoleProvider.dart'; +import '../screens/staff/Enquiry/Proposal_QuickCreation/Proposal_QuickCreation.dart'; +import '../themes/indicators/side_Drawer_Panel.dart'; class TopBar extends ConsumerStatefulWidget implements PreferredSizeWidget { final String title; @@ -141,6 +143,32 @@ class _TopBarState extends ConsumerState { ), ], SizedBox(width: 10), + if (role != 'Accounts') ...[ + // Proposal button + Container( + width: 36, + height: 36, + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(8), + border: Border.all(color: Colors.grey.shade200, width: 1), + ), + child: IconButton( + padding: EdgeInsets.zero, + icon: const Icon(Icons.quora, color: Colors.black87, size: 20), + onPressed: () { + SideDrawerPanel.show( + context: context, + title: 'Quote Creation', + child: CreateProposal_Quick(), + ); + }, + tooltip: 'Quick Quote', + ), + ), + + const SizedBox(width: 12), + ], Container( width: 40, height: 40, diff --git a/pubspec.lock b/pubspec.lock index abbeba9..d77ec7b 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -700,26 +700,26 @@ packages: dependency: transitive description: name: leak_tracker - sha256: "33e2e26bdd85a0112ec15400c8cbffea70d0f9c3407491f672a2fad47915e2de" + sha256: "6bb818ecbdffe216e81182c2f0714a2e62b593f4a4f13098713ff1685dfb6ab0" url: "https://pub.dev" source: hosted - version: "11.0.2" + version: "10.0.9" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: "1dbc140bb5a23c75ea9c4811222756104fbcd1a27173f0c34ca01e16bea473c1" + sha256: f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573 url: "https://pub.dev" source: hosted - version: "3.0.10" + version: "3.0.9" leak_tracker_testing: dependency: transitive description: name: leak_tracker_testing - sha256: "8d5a2d49f4a66b49744b23b018848400d23e54caf9463f4eb20df3eb8acb2eb1" + sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" url: "https://pub.dev" source: hosted - version: "3.0.2" + version: "3.0.1" lints: dependency: transitive description: @@ -1297,10 +1297,10 @@ packages: dependency: transitive description: name: test_api - sha256: "522f00f556e73044315fa4585ec3270f1808a4b186c936e612cab0b565ff1e00" + sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd url: "https://pub.dev" source: hosted - version: "0.7.6" + version: "0.7.4" toastification: dependency: "direct main" description: @@ -1417,10 +1417,10 @@ packages: dependency: transitive description: name: vector_math - sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b + sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" url: "https://pub.dev" source: hosted - version: "2.2.0" + version: "2.1.4" vm_service: dependency: transitive description: