diff --git a/lib/core/routing/app_router.dart b/lib/core/routing/app_router.dart index 69fb3f0..f3b5dae 100644 --- a/lib/core/routing/app_router.dart +++ b/lib/core/routing/app_router.dart @@ -5,6 +5,7 @@ import 'package:nhance_partner/core/routing/routes.dart'; import 'package:nhance_partner/presentation/screens/Enquiry/enquiryList.dart'; import 'package:nhance_partner/presentation/screens/UserManagement/Profile/profile_web.dart'; import 'package:nhance_partner/presentation/screens/dashboard/dashboard.dart'; +import 'package:nhance_partner/presentation/screens/handler/enquiryList.dart'; import '../../data/services/auth_service.dart'; import '../../presentation/providers/manager_provider.dart'; import '../../presentation/screens/Enquiry/enquiry/tabs.dart'; @@ -155,6 +156,10 @@ final GoRouter appRouter = GoRouter( path: AppRoutes.enquiryForStaff, builder: (context, state) => const EnquiryStaff(), ), + GoRoute( + path: AppRoutes.enquiryHandlerLst, + builder: (context, state) => const EnquiryHandler(), + ), ], redirect: (context, state) async { final loggedIn = await AuthService.isLoggedIn(); diff --git a/lib/core/routing/routes.dart b/lib/core/routing/routes.dart index dea9dc1..08c534f 100644 --- a/lib/core/routing/routes.dart +++ b/lib/core/routing/routes.dart @@ -12,6 +12,8 @@ class AppRoutes { static const String enquiryForStaff = '/enquiryForStaff'; static const String tabEnquiry = '/tabEnquiry'; + static const String enquiryHandlerLst = '/enquiryHandlerLst'; + static const String claimlist = '/claimList'; static const String endosement = '/endosement'; static const String policylist = '/policylist'; diff --git a/lib/core/services/api_service.dart b/lib/core/services/api_service.dart index 98fa871..332e374 100644 --- a/lib/core/services/api_service.dart +++ b/lib/core/services/api_service.dart @@ -348,6 +348,8 @@ class ApiService { if (role == 'manager') { url = Uri.parse('${Env.apiUrl}dashboard/managerDashboard?manager_id=$id'); + } else if (role == 'handler') { + url = Uri.parse('${Env.apiUrl}dashboard/handlerDashboard?handler_id=$id'); } else if (role == 'staff') { url = Uri.parse('${Env.apiUrl}dashboard/staffDashboard?staff_id=$id'); } else { @@ -465,7 +467,7 @@ class ApiService { // ------------------------------------ STAFF ----------------------------------------------------- - Future> fetchStaffUserList(int managerId) async { + Future> fetchStaffUserList(int managerId, role) async { // print(_token); if (_token == null) { await _initializeToken(); @@ -475,9 +477,15 @@ class ApiService { // 'https://venbait.in/nhance/partner/dev/api/staff/staffList?manager_id=${managerId}', // ); - final url = Uri.parse( - '${Env.apiUrl}staff/staffList?manager_id=${managerId}', - ); + dynamic url; + if (role == 'manager') { + print('manager'); + url = Uri.parse('${Env.apiUrl}staff/staffList?manager_id=${managerId}'); + } else { + print('handler'); + url = Uri.parse('${Env.apiUrl}staff/staffList?handler_id=${managerId}'); + } + final headers = { 'Authorization': 'Bearer $_token' ?? '', 'App-Signature': 'nhance-partner-2025-signature-35468846JRhH551HK', @@ -871,4 +879,50 @@ class ApiService { final response = await _makeGetRequest(url, headers); return response; } + + Future> fetchHandlerNameDropDown(id) async { + print('fetchHandlerNameDropDown'); + if (_token == null) { + await _initializeToken(); + } + + dynamic url; + print('fetchHandlerNameDropDown 1'); + url = Uri.parse( + '${Env.apiUrl}staff/handlerListForStaffCreationDropdown?manager_id=$id', + ); + print('fetchHandlerNameDropDown 2'); + final headers = { + 'Authorization': 'Bearer $_token' ?? '', + 'App-Signature': 'nhance-partner-2025-signature-35468846JRhH551HK', + }; + print('fetchHandlerNameDropDown 3'); + final response = await _makeGetRequest(url, headers); + print('fetchHandlerNameDropDown 4 - $response'); + return response; + } + + Future> fetchStaffListForEnquiryAssignDropDown( + id, + ) async { + print('fetchHandlerNameDropDown'); + if (_token == null) { + await _initializeToken(); + } + + dynamic url; + print('fetchHandlerNameDropDown 1'); + url = Uri.parse( + '${Env.apiUrl}/staff/staffListForEnquiryAssignDropdown?handler_id=$id', + ); + print('fetchHandlerNameDropDown 2'); + final headers = { + 'Authorization': 'Bearer $_token' ?? '', + 'App-Signature': 'nhance-partner-2025-signature-35468846JRhH551HK', + }; + print('fetchHandlerNameDropDown 3'); + final response = await _makeGetRequest(url, headers); + print('fetchHandlerNameDropDown 4 - $response'); + return response; + } } diff --git a/lib/presentation/providers/manager_provider.dart b/lib/presentation/providers/manager_provider.dart index f3ef0db..499244d 100644 --- a/lib/presentation/providers/manager_provider.dart +++ b/lib/presentation/providers/manager_provider.dart @@ -1,5 +1,6 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; final managerIdProvider = StateProvider((ref) => null); +final handlerIdProvider = StateProvider((ref) => null); final userIdProvider = StateProvider((ref) => null); final enquiryIdProvider = StateProvider((ref) => null); diff --git a/lib/presentation/screens/Enquiry/enquiry/policy_popup.dart b/lib/presentation/screens/Enquiry/enquiry/policy_popup.dart index fcd0e91..062c04b 100644 --- a/lib/presentation/screens/Enquiry/enquiry/policy_popup.dart +++ b/lib/presentation/screens/Enquiry/enquiry/policy_popup.dart @@ -1,6 +1,7 @@ import 'dart:convert'; import 'package:dropdown_search/dropdown_search.dart'; +import 'package:file_picker/file_picker.dart'; import 'package:flutter/material.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:http/http.dart' as http; @@ -12,6 +13,7 @@ import '../../../../data/services/auth_service.dart'; import '../../../../data/utils/toastNotification.dart'; import '../../../../data/utils/validators.dart'; import '../../../layouts/responsive_layout.dart'; +import '../../../themes/indicators/customizd_file_upload.dart'; import '../../../themes/indicators/input_field_decoration.dart'; import '../../../themes/indicators/text_field_theme.dart'; @@ -41,6 +43,16 @@ class _AddDialogState extends State { late TextEditingController controller; Map controllers = {}; + String? selectedClaimFileNames; + String? passportFileUrlFromApi; + String? selectedClaimId; + PlatformFile? docUploadedFile; + + String? selectedFileNames; + String? passportEndorsFileUrlFromApi; + String? selectedEndorsId; + PlatformFile? docUploadedEndorsFile; + final _formKeyClaims = GlobalKey(); final _formKeyEndrosment = GlobalKey(); final GlobalKey>> dropDownKey = @@ -204,7 +216,7 @@ class _AddDialogState extends State { } } - Future createUserData(data, val) async { + Future createUserData1(data, val) async { // final bool isUpdating = widget.id != null && widget.id != 'create'; final String apiUrldata; @@ -253,6 +265,115 @@ class _AddDialogState extends State { } } + Future createUserData(data, val) async { + final Uri uri = (val == 'Claims') + ? Uri.parse('${Env.apiUrl}claim/createClaim') + : Uri.parse('${Env.apiUrl}endorsement/createEndorsement'); + + if (_token == null) { + throw Exception('Token not found. Please log in.'); + } + + // Use MultipartRequest (POST only) + final request = http.MultipartRequest('POST', uri); + request.headers['Authorization'] = 'Bearer $_token'; + request.headers['app-signature'] = + 'nhance-partner-2025-signature-35468846JRhH551HK'; + + print("USerDAta - $data"); + + // userData.forEach((key, value) { + // request.fields[key] = value.toString(); + // print("✅ Encoded travel_details2: ${request.fields[key]}"); + // }); + + data.forEach((key, value) { + // if (key != 'certificate_file_name') { + request.fields[key] = value.toString(); + print("✅ Encoded $key: ${request.fields[key]}"); + // } + }); + if (val == 'Claims') { + // Attach file if selected + if (docUploadedFile != null) { + try { + if (docUploadedFile!.bytes != null) { + final multipartFile = http.MultipartFile.fromBytes( + 'claim_file_name', + docUploadedFile!.bytes!, + filename: docUploadedFile!.name, + ); + request.files.add(multipartFile); + } + // else if (docUploadedFile!.path != null) { + // final multipartFile = await http.MultipartFile.fromPath( + // 'certificate_file_name', + // docUploadedFile!.path!, + // filename: docUploadedFile!.name, + // ); + // request.files.add(multipartFile); + // } + print("📎 File attached: ${docUploadedFile!.name}"); + } catch (e) { + print("❌ Failed to attach file: $e"); + } + } + } else { + // Attach file if selected + if (docUploadedEndorsFile != null) { + try { + if (docUploadedEndorsFile!.bytes != null) { + final multipartFile = http.MultipartFile.fromBytes( + 'endorsement_file_name', + docUploadedEndorsFile!.bytes!, + filename: docUploadedEndorsFile!.name, + ); + request.files.add(multipartFile); + } + // else if (docUploadedEndorsFile!.path != null) { + // final multipartFile = await http.MultipartFile.fromPath( + // 'endorsement_file_name', + // docUploadedEndorsFile!.path!, + // filename: docUploadedEndorsFile!.name, + // ); + // request.files.add(multipartFile); + // } + print("📎 File attached: ${docUploadedEndorsFile!.name}"); + } catch (e) { + print("❌ Failed to attach file: $e"); + } + } + } + print(" Sending request with fields: ${request.fields}"); + + try { + final streamedResponse = await request.send(); + final response = await http.Response.fromStream(streamedResponse); + print("Response status: ${response.statusCode}"); + print("Response body: ${response.body}"); + + if (response.statusCode == 200 || response.statusCode == 201) { + print("✅ Partner submitted successfully!"); + + print("Response: ${response.body}"); + ToastHelper.showSuccessToast(context, 'Saved Successfully'); + Navigator.of(context).pop(); + + widget.onSubmit("success"); + } else { + final responseBody = jsonDecode(response.body); + dynamic msg = responseBody['data']; + + print("Failed to submit plan. Status: ${response.statusCode}"); + print("Error: ${response.body}"); + + ToastHelper.showErrorToast(context, 'Failed To Save'); + } + } catch (e) { + print("🔥 Error submitting user: $e"); + } + } + @override Widget build(BuildContext context) { return AlertDialog( @@ -296,24 +417,19 @@ class _AddDialogState extends State { ], ), actions: [ - Center( - child: GestureDetector( - onTap: () { - handleDone(widget.title); - // widget.onSubmit(controller.text.trim()); - // Navigator.of(context).pop(); - }, - child: Container( - padding: const EdgeInsets.symmetric( - horizontal: 25.0, - vertical: 8, - ), - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(8.0), - color: const Color(0xFF425B5B), - ), - child: const Text('Done', style: TextStyle(color: Colors.white)), + GestureDetector( + onTap: () { + handleDone(widget.title); + // widget.onSubmit(controller.text.trim()); + // Navigator.of(context).pop(); + }, + child: Container( + padding: const EdgeInsets.symmetric(horizontal: 25.0, vertical: 8), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(8.0), + color: const Color(0xFF425B5B), ), + child: const Text('Save', style: TextStyle(color: Colors.white)), ), ), ], @@ -328,6 +444,7 @@ class _AddDialogState extends State { children: [ buildClaimType(context), buildPolicyNumber(context), + buildUploadDocumentClaims(context), buildClaimsDesc(context), ], ), @@ -339,7 +456,11 @@ class _AddDialogState extends State { return Form( key: _formKeyEndrosment, child: Column( - children: [buildEndrosType(context), buildEndrosRemarks(context)], + children: [ + buildEndrosType(context), + buildUploadDocumentEndorsment(context), + buildEndrosRemarks(context), + ], ), ); } @@ -472,7 +593,88 @@ class _AddDialogState extends State { ); } - // ------------------------- Endrosment Part ----------------------------------- + Widget buildUploadDocumentClaims(BuildContext context) { + return Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text("Upload Document", style: _textStyle), + SizedBox(width: 10), + ThemedUploadField( + backgroundColor: Color(0xFFEDF6F5), + hintText: selectedClaimFileNames ?? "Upload Document", + txtwidth: ResponsiveLayout.isMobile(context) + ? null + : MediaQuery.of(context).size.width * 0.26, + onFileSelected: (fileName, file) { + print("Picked file: $fileName (${file.size} bytes)"); + setState(() { + docUploadedFile = file; + }); + }, + ), + // Column( + // mainAxisAlignment: MainAxisAlignment.end, + // crossAxisAlignment: CrossAxisAlignment.end, + // children: [ + // ThemedUploadField( + // hintText: selectedClaimFileNames ?? "Upload Document", + // txtwidth: MediaQuery.of(context).size.width * 0.26, + // onFileSelected: (fileName, file) { + // print("Picked file: $fileName (${file.size} bytes)"); + // setState(() { + // docUploadedFile = file; + // }); + // }, + // ), + // // const SizedBox(height: 5), + // // + // // if (docUploadedFile != null || passportFileUrlFromApi != null) + // // Container( + // // // color: Colors.white, + // // child: Row( + // // children: [ + // // GestureDetector( + // // onTap: () => apiService.downloadFile( + // // apiUrl: + // // 'api/agent/downloadAgentCertificateFile?agent_id=$selectedClaimId', + // // apiId: selectedClaimId, + // // localFile: docUploadedFile, + // // fileName: selectedClaimFileNames, + // // ), + // // child: Container( + // // padding: const EdgeInsets.all(5), + // // decoration: BoxDecoration( + // // borderRadius: BorderRadius.circular(5), + // // color: Color(0xFF425B5B), + // // // color: Colors.green.shade300, + // // ), + // // child: Row( + // // children: const [ + // // Text( + // // "Download", + // // style: TextStyle( + // // fontSize: 12, + // // fontWeight: FontWeight.w200, + // // color: Colors.white, + // // ), + // // ), + // // SizedBox(width: 5), + // // Icon(Icons.download, size: 13, color: Colors.white), + // // ], + // // ), + // // ), + // // ), + // // ], + // // ), + // // ), + // ], + // ), + ], + ); + } + + //------------------------- Endrosment Part ----------------------------------- Widget buildEndrosType(context) { Map? selectedEndorsementd = filteredEndrosmentData @@ -586,10 +788,91 @@ class _AddDialogState extends State { ); } + Widget buildUploadDocumentEndorsment(context) { + return Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text("Upload Document", style: _textStyle), + SizedBox(width: 10), + ThemedUploadField( + backgroundColor: Color(0xFFEDF6F5), + hintText: selectedFileNames ?? "Upload Document", + txtwidth: ResponsiveLayout.isMobile(context) + ? null + : MediaQuery.of(context).size.width * 0.26, + onFileSelected: (fileName, file) { + print("Picked file: $fileName (${file.size} bytes)"); + setState(() { + docUploadedEndorsFile = file; + }); + }, + ), + // Column( + // mainAxisAlignment: MainAxisAlignment.end, + // crossAxisAlignment: CrossAxisAlignment.end, + // children: [ + // ThemedUploadField( + // hintText: selectedFileNames ?? "Upload Document", + // txtwidth: MediaQuery.of(context).size.width * 0.26, + // onFileSelected: (fileName, file) { + // print("Picked file: $fileName (${file.size} bytes)"); + // setState(() { + // docUploadedEndorsFile = file; + // }); + // }, + // ), + // // const SizedBox(height: 5), + // // + // // if (docUploadedEndorsFile != null || passportEndorsFileUrlFromApi != null) + // // Container( + // // // color: Colors.white, + // // child: Row( + // // children: [ + // // + // // GestureDetector( + // // onTap: () => apiService.downloadFile( + // // apiUrl: + // // 'api/agent/downloadAgentCertificateFile?agent_id=$selectedEndorsId', + // // apiId: selectedEndorsId, + // // localFile: docUploadedEndorsFile, + // // fileName: selectedFileNames, + // // ), + // // child: Container( + // // padding: const EdgeInsets.all(5), + // // decoration: BoxDecoration( + // // borderRadius: BorderRadius.circular(5), + // // color: Color(0xFF425B5B), + // // // color: Colors.green.shade300, + // // ), + // // child: Row( + // // children: const [ + // // Text( + // // "Download", + // // style: TextStyle( + // // fontSize: 12, + // // fontWeight: FontWeight.w200, + // // color: Colors.white, + // // ), + // // ), + // // SizedBox(width: 5), + // // Icon(Icons.download, size: 13, color: Colors.white), + // // ], + // // ), + // // ), + // // ), + // // ], + // // ), + // // ), + // ], + // ), + ], + ); + } // ------------------- STyle --------------------------------- static final TextStyle _textStyle = TextStyle( - fontSize: 14, + fontSize: 12, fontWeight: FontWeight.w600, ); } diff --git a/lib/presentation/screens/Enquiry/enquiry/tabs.dart b/lib/presentation/screens/Enquiry/enquiry/tabs.dart index 99af8cd..432aafd 100644 --- a/lib/presentation/screens/Enquiry/enquiry/tabs.dart +++ b/lib/presentation/screens/Enquiry/enquiry/tabs.dart @@ -3,6 +3,7 @@ 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 'package:shared_preferences/shared_preferences.dart'; import '../../../../core/routing/routes.dart'; @@ -28,6 +29,7 @@ class TabEnquiryListState extends ConsumerState { bool isLoading = true; late ApiService apiService; ScrollController _scrollController = ScrollController(); + dynamic role; @override void initState() { @@ -40,7 +42,7 @@ class TabEnquiryListState extends ConsumerState { Future.microtask(() { final id = ref.read(enquiryIdProvider); - + role = ref.read(userRoleProvider); print("IntialID - $id"); if (id != null) { _loadData(id); @@ -228,8 +230,14 @@ class TabEnquiryListState extends ConsumerState { child: GestureDetector( onTap: () { // context.go(AppRoutes.dashboard); - ref.read(enquiryIdProvider.notifier).state = null; - context.go(AppRoutes.enquiryLst); + + if (role == 'handler') { + ref.read(enquiryIdProvider.notifier).state = null; + context.go(AppRoutes.enquiryHandlerLst); + } else { + ref.read(enquiryIdProvider.notifier).state = null; + context.go(AppRoutes.enquiryLst); + } }, child: Row( children: [ @@ -421,8 +429,15 @@ class TabEnquiryListState extends ConsumerState { Tooltip( message: 'Back', child: IconButton( - icon: const Icon(Icons.arrow_left_sharp,size: 35,color: Color(0xFF425B5B)), - onPressed: () { ref.read(enquiryIdProvider.notifier).state = null; context.go(AppRoutes.enquiryLst); }, + icon: const Icon( + Icons.arrow_left_sharp, + size: 35, + color: Color(0xFF425B5B), + ), + onPressed: () { + ref.read(enquiryIdProvider.notifier).state = null; + context.go(AppRoutes.enquiryLst); + }, splashRadius: 28, hoverColor: Colors.black12, padding: const EdgeInsets.all(8), diff --git a/lib/presentation/screens/UserManagement/Staff/staff.dart b/lib/presentation/screens/UserManagement/Staff/staff.dart index 8f1bb07..20836ba 100644 --- a/lib/presentation/screens/UserManagement/Staff/staff.dart +++ b/lib/presentation/screens/UserManagement/Staff/staff.dart @@ -1,6 +1,7 @@ import 'dart:convert'; // import 'dart:io' as html; import 'dart:typed_data'; // Import for Uint8List +import 'package:dropdown_search/dropdown_search.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; @@ -15,10 +16,13 @@ import '../../../../core/services/api_service.dart'; import '../../../../data/services/auth_service.dart'; import '../../../../data/utils/validators.dart'; import '../../../layouts/main_layout.dart'; +import '../../../layouts/responsive_layout.dart'; import '../../../providers/manager_provider.dart'; import 'package:universal_html/html.dart' as html; +import '../../../themes/indicators/input_field_decoration.dart'; + class Staff extends ConsumerStatefulWidget { final String? id; const Staff({super.key, this.id}); @@ -29,6 +33,13 @@ class Staff extends ConsumerStatefulWidget { class StaffState extends ConsumerState { final _formKey = GlobalKey(); late ApiService apiService; + + final GlobalKey>> dropDownKey = + GlobalKey>>(); + + final GlobalKey>> + dropDownKeyHandler = GlobalKey>>(); + List tabHeader = ['name', 'email', 'mobile', 'code']; late String isActive = "1"; html.File? passportFile; @@ -36,6 +47,16 @@ class StaffState extends ConsumerState { String? passportFileUrlFromApi; String? selectedId; String? _token; + bool isLoading = false; + bool showHandler = false; + + String? selectedRole; + List> filteredRolesData = []; + List> getRolesData = []; + + String? selectedHandler; + List> filteredHandlersData = []; + List> getHandlersData = []; Map controllers = {}; @@ -47,9 +68,10 @@ class StaffState extends ConsumerState { "name": controllers["name"]?.text, "email": controllers["email"]?.text, "mobile": controllers["mobile"]?.text, - "emp_id": controllers["code"]?.text, + // "emp_id": controllers["code"]?.text, "is_active": isActive, - "role_id": "2", // By default role 2 Staff, No fields Required + "role_id": selectedRole, // By default role 2 Staff, No fields Required + "handler_id": selectedHandler, "manager_id": userId, }; return data; @@ -63,12 +85,18 @@ class StaffState extends ConsumerState { controllers[field] = TextEditingController(); } + _initializeToken(); + updateData(); + getRole(); + Future.microtask(() { managerId = ref.watch(managerIdProvider); userId = ref.watch(userIdProvider); + if (managerId != null) { + print('hansles'); + getHandlers(managerId); + } }); - _initializeToken(); - updateData(); } Future _initializeToken() async { @@ -88,7 +116,10 @@ class StaffState extends ConsumerState { controllers['name']?.text = data['name'] ?? ''; controllers['email']?.text = data['email'] ?? ''; controllers['mobile']?.text = data['mobile'] ?? ''; - controllers['code']?.text = data['emp_id'] ?? ''; + selectedRole = data['role_id'] ?? ''; + selectedHandler = data['handler_id'] ?? ''; + + // controllers['code']?.text = data['emp_id'] ?? ''; isActive = data["is_active"]; String? apiDocPath = data["certificate_file_name"]; @@ -201,6 +232,69 @@ class StaffState extends ConsumerState { } } + Future getRole() async { + print('Handlers called'); + setState(() { + isLoading = true; + }); + + try { + final response = await apiService.fetchMasterDropDown('staffRole'); + + if (response['status'] == 200) { + print('getRole - ${response['data']}'); + setState(() { + getRolesData = List>.from(response['data']); + print('API Data - $getRolesData'); + + filteredRolesData = List.from(getRolesData); + print('originalData - $filteredRolesData'); + }); + } else { + getRolesData = []; + filteredRolesData = []; + } + } catch (e) { + print('Exception occurred: $e'); + } finally { + setState(() { + isLoading = false; + }); + } + } + + Future getHandlers(managerId) async { + print('Handlers called'); + setState(() { + isLoading = true; + }); + + try { + final response = await apiService.fetchHandlerNameDropDown(managerId); + + if (response['code'] == 200 || response['status'] == 'success') { + print('getHandlers -'); + print('getHandlers - ${response['data']}'); + setState(() { + getHandlersData = List>.from(response['data']); + print('API HandlersData - $getHandlersData'); + + filteredHandlersData = List.from(getHandlersData); + print('HandlersoriginalData - $filteredHandlersData'); + }); + } else { + getHandlersData = []; + filteredHandlersData = []; + } + } catch (e) { + print('Exception occurred: $e'); + } finally { + setState(() { + isLoading = false; + }); + } + } + @override void dispose() { for (var controller in controllers.values) { @@ -331,11 +425,20 @@ class StaffState extends ConsumerState { children: [ Expanded(child: buildPhNumber()), SizedBox(width: 25), - Expanded(child: buildId()), + Expanded(child: buildRole(context)), ], ), SizedBox(height: 20), + if (showHandler) ...[ + Row( + children: [ + Expanded(child: buildHandler(context)), + SizedBox(width: 25), + Expanded(child: SizedBox.shrink()), + ], + ), + ], // Row( // children: [ // Expanded(child: buildAddress()), @@ -408,16 +511,217 @@ class StaffState extends ConsumerState { ); } - Widget buildAddress() { + // Widget buildAddress() { + // return Row( + // mainAxisAlignment: MainAxisAlignment.spaceBetween, + // children: [ + // Text("Role*", style: _textStyle), + // SizedBox(width: 10), + // ThemedFormField( + // controller: controllers['address']!, + // + // txtwidth: MediaQuery.of(context).size.width * 0.26, + // ), + // ], + // ); + // } + + Widget buildRole(BuildContext context) { + Map? selectedroleVal = filteredRolesData.firstWhere( + (item) => item['id'].toString() == selectedRole, + orElse: () => {}, + ); + + if (selectedroleVal.isNotEmpty && + selectedroleVal['role'] != null && + selectedroleVal['role'] == 'Staff') { + showHandler = true; + } else { + showHandler = false; + } return Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Text("Role*", style: _textStyle), + Text("Role *", style: _textStyle), SizedBox(width: 10), - ThemedFormField( - controller: controllers['address']!, + Container( + decoration: BoxDecoration( + // color: Colors.white, + borderRadius: BorderRadius.circular(10.0), + ), + width: ResponsiveLayout.isMobile(context) + ? null + : MediaQuery.of(context).size.width * 0.26, + // height: 40, + child: DropdownSearch>( + key: dropDownKey, + selectedItem: selectedroleVal.isNotEmpty ? selectedroleVal : null, + items: (filter, infiniteScrollProps) { + return filteredRolesData; + }, - txtwidth: MediaQuery.of(context).size.width * 0.26, + itemAsString: (val) => val['role'].toString(), // what to show + compareFn: (item, selectedItem) => + item['id'] == selectedItem['id'], // ✅ compare by id + validator: (val) { + if (val == null) { + return "Required"; // ✅ error message + } + return null; + }, + decoratorProps: DropDownDecoratorProps( + decoration: + AppInputDecorations.dropdownDecoration( + label: "Select Role", + ).copyWith( + filled: true, + fillColor: + Colors.white, // 👈 makes the dropdown input white + ), + ), + + popupProps: PopupProps.menu( + fit: FlexFit.loose, + constraints: BoxConstraints(maxHeight: 250), + menuProps: MenuProps( + backgroundColor: + Colors.white, // 👈 sets dropdown background to white + ), + showSearchBox: true, + searchFieldProps: TextFieldProps( + decoration: InputDecoration( + filled: true, + fillColor: Colors.white, + hintText: "Search Role...", + enabledBorder: OutlineInputBorder( + borderSide: BorderSide( + color: Colors.white, + ), // 👈 Normal border + ), + focusedBorder: OutlineInputBorder( + borderSide: BorderSide( + color: Colors.white, + width: 1.5, + ), // 👈 Focused border + ), + ), + ), + // constraints: BoxConstraints(), + ), + + onChanged: (val) { + if (val != null) { + print("Selected Role : ${val['role']}"); + print("Id: ${val['id']}"); + selectedRole = val['id']; + if (val['role'] == 'Staff') { + setState(() { + showHandler = true; + }); + } else { + setState(() { + showHandler = false; + }); + } + + // controllers['agentId']?.text = val['agent_code']; + // agentId = agent['id']; + } + }, + ), + ), + ], + ); + } + + Widget buildHandler(BuildContext context) { + Map? selectedHandlered = filteredHandlersData.firstWhere( + (item) => item['id'].toString() == selectedHandler, + orElse: () => {}, + ); + + return Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text("Handler Name *", style: _textStyle), + SizedBox(width: 10), + Container( + decoration: BoxDecoration( + // color: Colors.white, + borderRadius: BorderRadius.circular(10.0), + ), + width: ResponsiveLayout.isMobile(context) + ? null + : MediaQuery.of(context).size.width * 0.26, + // height: 40, + child: DropdownSearch>( + key: dropDownKeyHandler, + selectedItem: selectedHandlered.isNotEmpty + ? selectedHandlered + : null, + items: (filter, infiniteScrollProps) { + return filteredHandlersData; + }, + + itemAsString: (val) => val['name'].toString(), // what to show + compareFn: (item, selectedItem) => + item['id'] == selectedItem['id'], // ✅ compare by id + validator: (val) { + if (val == null) { + return "Required"; // ✅ error message + } + return null; + }, + decoratorProps: DropDownDecoratorProps( + decoration: + AppInputDecorations.dropdownDecoration( + label: "Select Handler", + ).copyWith( + filled: true, + fillColor: + Colors.white, // 👈 makes the dropdown input white + ), + ), + + popupProps: PopupProps.menu( + fit: FlexFit.loose, + constraints: BoxConstraints(maxHeight: 250), + menuProps: MenuProps( + backgroundColor: + Colors.white, // 👈 sets dropdown background to white + ), + showSearchBox: true, + searchFieldProps: TextFieldProps( + decoration: InputDecoration( + filled: true, + fillColor: Colors.white, + hintText: "Search Handler...", + enabledBorder: OutlineInputBorder( + borderSide: BorderSide( + color: Colors.white, + ), // 👈 Normal border + ), + focusedBorder: OutlineInputBorder( + borderSide: BorderSide( + color: Colors.white, + width: 1.5, + ), // 👈 Focused border + ), + ), + ), + // constraints: BoxConstraints(), + ), + + onChanged: (val) { + if (val != null) { + print("Selected Handler : ${val['name']}"); + print("Id: ${val['id']}"); + selectedHandler = val['id']; + // controllers['agentId']?.text = val['agent_code']; + // agentId = agent['id']; + } + }, + ), ), ], ); diff --git a/lib/presentation/screens/UserManagement/Staff/staffList.dart b/lib/presentation/screens/UserManagement/Staff/staffList.dart index 5e7cd46..b918323 100644 --- a/lib/presentation/screens/UserManagement/Staff/staffList.dart +++ b/lib/presentation/screens/UserManagement/Staff/staffList.dart @@ -2,6 +2,7 @@ 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:nhance_partner/presentation/providers/userRoleProvider.dart'; import '../../../../core/routing/routes.dart'; import '../../../../core/services/api_service.dart'; @@ -26,6 +27,8 @@ class StaffListState extends ConsumerState { int itemsPerPage = 10; late ApiService apiService; dynamic managerId; + dynamic role; + dynamic prefid; // List> dataVal = []; List> getStaffData = []; List> originalData = []; @@ -39,10 +42,14 @@ class StaffListState extends ConsumerState { apiService = ApiService(); Future.microtask(() { - final id = ref.read(managerIdProvider); - print("E43 => mId: $id"); - if (id != null) { - getStaffList(id); + final data1 = ref.read(managerIdProvider); + final data2 = ref.read(handlerIdProvider); + print("Edata1 => mId: $data1 -2 : $data2"); + prefid = data2 ?? data1; + role = ref.read(userRoleProvider); + print("E43 => mId: $prefid"); + if (prefid != null && role != null) { + getStaffList(prefid, role); } }); // getStaffList(); @@ -83,16 +90,18 @@ class StaffListState extends ConsumerState { return (item['name'] ?? '-').toLowerCase().contains( query.toLowerCase(), ) || - (item['email'] ?? '-').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(), - ) || + (item['emp_id'] ?? item['agent_code'] ?? '-') + .toLowerCase() + .contains(query.toLowerCase()) || isActiveStatus.contains(query.toLowerCase()); }).toList(); }); @@ -100,14 +109,15 @@ class StaffListState extends ConsumerState { final TextEditingController _searchStaffController = TextEditingController(); - Future getStaffList(int managerId) async { - print('E104 => Fns called => $managerId'); + Future getStaffList(int id, role) async { + print('E104 => Fns called => $id'); + print('E104 => Fns role => $role'); setState(() { isLoading = true; }); try { - final response = await apiService.fetchStaffUserList(managerId); + final response = await apiService.fetchStaffUserList(id, role); if (response['status'] == 'success') { print('E113 => getStaffListData => ${response['data']}'); @@ -184,8 +194,14 @@ class StaffListState extends ConsumerState { Tooltip( message: 'Back', child: IconButton( - icon: const Icon(Icons.arrow_left_sharp,size: 25,color: Color(0xFF425B5B)), - onPressed: () { context.go(AppRoutes.dashboard); }, + 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), @@ -244,7 +260,7 @@ class StaffListState extends ConsumerState { "name", "email", "mobile", - "emp_id", + "role", "is_active", ], ), @@ -300,6 +316,10 @@ class StaffListState extends ConsumerState { flex: 2, child: Text('Staff Name', style: _headerStyle), ), + Expanded( + flex: 1, + child: Text('Role', style: _headerStyle), + ), Expanded( flex: 3, child: Text('Email', style: _headerStyle), @@ -308,10 +328,7 @@ class StaffListState extends ConsumerState { flex: 2, child: Text('Phone Number', style: _headerStyle), ), - Expanded( - flex: 1, - child: Text('Employee Id', style: _headerStyle), - ), + Expanded( flex: 1, child: Text('Status', style: _headerStyle), @@ -404,6 +421,8 @@ class StaffListState extends ConsumerState { children: [ Expanded(flex: 1, child: Text('$sno' ?? '-', style: _dataBold)), Expanded(flex: 2, child: Text(item['name'] ?? '-', style: _dataBold)), + Expanded(flex: 1, child: Text(item['role'] ?? '-', style: _dataBold)), + Expanded( flex: 3, child: Text(item['email'] ?? '-', style: _dataBold), @@ -412,10 +431,6 @@ class StaffListState extends ConsumerState { flex: 2, child: Text(item['mobile'] ?? '-', style: _dataBold), ), - Expanded( - flex: 1, - child: Text(item['emp_id'] ?? '-', style: _dataBold), - ), Expanded( flex: 1, diff --git a/lib/presentation/screens/dashboard/dashboard.dart b/lib/presentation/screens/dashboard/dashboard.dart index d415568..e6d7cd5 100644 --- a/lib/presentation/screens/dashboard/dashboard.dart +++ b/lib/presentation/screens/dashboard/dashboard.dart @@ -68,6 +68,7 @@ class _DashboardScreenState extends ConsumerState { int selectedTabIndex = 0; List> currentList = []; + List> staffQuotationsPendingList = []; // cross verify List> quotationsPendingList = []; // cross verify List> policiesPendingList = []; // cross verify List> awaitingApprovalList = []; // cross verify @@ -262,6 +263,16 @@ class _DashboardScreenState extends ConsumerState { }); } + if (data['staff_level_pending_summary'] != null) { + print('staff_level_pending_summary -'); + staffQuotationsPendingList = List>.from( + (data['staff_level_pending_summary'] as List).map( + (item) => Map.from(item), + ), + ); + print('staff_level_pending_summary - $staffQuotationsPendingList'); + } + if (data['quotations_pending'] != null) { quotationsPendingList = List>.from( (data['quotations_pending'] as List).map( @@ -490,7 +501,8 @@ class _DashboardScreenState extends ConsumerState { scrollDirection: Axis.vertical, child: Column( children: [ - if (role != 'staff') + // if (role != 'staff') + if (role != 'staff' && role != 'handler') Row( children: [ Expanded( @@ -535,133 +547,145 @@ class _DashboardScreenState extends ConsumerState { : (role == 'manager') ? MediaQuery.of(context).size.height * 0.5 : MediaQuery.of(context).size.height * - 0.85, // 350 adjust height as needed - child: Row( - children: [ - Expanded( - child: (role == 'agent') - ? agentPendings( + 0.5, // 350 adjust height as needed + child: (role == 'manager' || role == 'handler') + ? Row( + children: [ + Expanded( + child: othersPendings( title: - "Awaiting Quotation (${quotationsPendingList.length})", - data: quotationsPendingList, - role: role!, - onEdit: (row) { - handleEdit( - row, - ); // parent method is called - }, - onStaffEdit: (row, flag) { - handleStaffEdit(row, flag); - }, - ) - : (role == 'manager') - ? othersPendings( - title: - "Awaiting Quotation (${quotationsPendingList.length})", - data: quotationsPendingList, + "Staff Proposal Pending List (${staffQuotationsPendingList.length})", + data: staffQuotationsPendingList, stringFlag: "Quotation", - ) - : agentPendings( - title: - "Awaiting Quotation (${quotationsPendingList.length})", - data: quotationsPendingList, role: role!, - onEdit: (row) { - handleEdit( - row, - ); // parent method is called - }, + ), + ), + ], + ) + : Row( + children: [ + Expanded( + child: (role == 'agent') + ? agentPendings( + title: + "Awaiting Quotation (${quotationsPendingList.length})", + data: quotationsPendingList, + role: role!, + onEdit: (row) { + handleEdit( + row, + ); // parent method is called + }, + onStaffEdit: (row, flag) { + handleStaffEdit(row, flag); + }, + ) + : (role == 'manager') + ? SizedBox.shrink() + : agentPendings( + title: + "Awaiting Quotation (${quotationsPendingList.length})", + data: quotationsPendingList, + role: role!, + onEdit: (row) { + handleEdit( + row, + ); // parent method is called + }, - onStaffEdit: (row, flag) { - handleStaffEdit(row, flag); - }, - ), - ), - const SizedBox(width: 16), - Expanded( - child: (role == 'agent') - ? agentPendings( - title: - "Awaiting Approval (${awaitingApprovalList.length})", - data: awaitingApprovalList, - role: role!, - onEdit: (row) { - handleEdit( - row, - ); // parent method is called - }, - onStaffEdit: (row, flag) { - handleStaffEdit(row, flag); - }, - ) - : (role == 'manager') - ? awaitingApproval( - title: - "Awaiting Approval (${awaitingApprovalList.length})", - data: awaitingApprovalList, - stringFlag: "Quotation", - ) - : agentPendings( - title: - "Awaiting Approval (${awaitingApprovalList.length})", - data: awaitingApprovalList, - role: role!, - onEdit: (row) { - handleEdit( - row, - ); // parent method is called - }, - onStaffEdit: (row, flag) { - handleStaffEdit(row, flag); - }, - ), - ), - const SizedBox(width: 16), - Expanded( - child: (role == 'agent') - ? agentPendings( - title: - "Awaiting Policy (${policiesPendingList.length})", - data: policiesPendingList, - stringFlag: "Policies", - role: role!, - onEdit: (row) { - handleEdit( - row, - ); // parent method is called - }, - onStaffEdit: (row, flag) { - handleStaffEdit(row, flag); - }, - ) - : (role == 'manager') - ? othersPendings( - title: - "Awaiting Policy (${policiesPendingList.length})", - data: policiesPendingList, - stringFlag: "Policies", - ) - : agentPendings( - title: - "Awaiting Policy (${policiesPendingList.length})", - data: policiesPendingList, - stringFlag: "Policies", - role: role!, - onEdit: (row) { - handleEdit( - row, - ); // parent method is called - }, - onStaffEdit: (row, flag) { - handleStaffEdit(row, flag); - }, - ), - ), - ], - ), + onStaffEdit: (row, flag) { + handleStaffEdit(row, flag); + }, + ), + ), + const SizedBox(width: 16), + Expanded( + child: (role == 'agent') + ? agentPendings( + title: + "Awaiting Approval (${awaitingApprovalList.length})", + data: awaitingApprovalList, + role: role!, + onEdit: (row) { + handleEdit( + row, + ); // parent method is called + }, + onStaffEdit: (row, flag) { + handleStaffEdit(row, flag); + }, + ) + : (role == 'manager') + ? SizedBox.shrink() + // othersPendings( + // title: + // "Awaiting Quotation (${quotationsPendingList.length})", + // data: quotationsPendingList, + // stringFlag: "Quotation", + // ) + : agentPendings( + title: + "Awaiting Approval (${awaitingApprovalList.length})", + data: awaitingApprovalList, + role: role!, + onEdit: (row) { + handleEdit( + row, + ); // parent method is called + }, + onStaffEdit: (row, flag) { + handleStaffEdit(row, flag); + }, + ), + ), + const SizedBox(width: 16), + Expanded( + child: (role == 'agent') + ? agentPendings( + title: + "Awaiting Policy (${policiesPendingList.length})", + data: policiesPendingList, + stringFlag: "Policies", + role: role!, + onEdit: (row) { + handleEdit( + row, + ); // parent method is called + }, + onStaffEdit: (row, flag) { + handleStaffEdit(row, flag); + }, + ) + : (role == 'staff') + ? + // ? othersPendings( + // title: + // "Awaiting Quotation (${quotationsPendingList.length})", + // data: quotationsPendingList, + // stringFlag: "Quotation", + // ) + agentPendings( + title: + "Awaiting Policy (${policiesPendingList.length})", + data: policiesPendingList, + stringFlag: "Policies", + role: role!, + onEdit: (row) { + handleEdit( + row, + ); // parent method is called + }, + onStaffEdit: (row, flag) { + handleStaffEdit(row, flag); + }, + ) + : SizedBox.shrink(), + ), + ], + ), ), const SizedBox(height: 10), - if (role == 'manager') + if (role == 'manager' || role == 'handler') SizedBox( // ✅ replace Expanded height: 350, // adjust height as needed @@ -670,7 +694,8 @@ class _DashboardScreenState extends ConsumerState { Expanded( child: UnassignedEnq( key: UniqueKey(), - title: "Unassigned Enquires", + title: "Unassigned Enquiries", + role: role!, data: unAssignedEnqList, onRefresh: refresh, ), @@ -1094,11 +1119,13 @@ class agentPendings extends StatelessWidget { class othersPendings extends StatelessWidget { final String title; final String stringFlag; + String role; final List> data; othersPendings({ Key? key, required this.title, + required this.role, required this.data, required this.stringFlag, }) : super(key: key); @@ -1137,6 +1164,16 @@ class othersPendings extends StatelessWidget { // style: _headerStyle, // ), // ), + if (role == 'manager') ...[ + Expanded( + flex: 2, + child: Text( + "Handler Name", + textAlign: TextAlign.center, + style: _headerStyle, + ), + ), + ], Expanded( flex: 2, child: Text( @@ -1145,25 +1182,49 @@ class othersPendings extends StatelessWidget { style: _headerStyle, ), ), + Expanded( flex: 2, child: Text( // stringFlag + " Issued", - 'Pending Count', + 'Num Of Enquiry Assigned', + textAlign: TextAlign.center, + style: _headerStyle, + ), + ), + Expanded( + flex: 2, + child: Text( + 'Awaiting Proposal', + textAlign: TextAlign.center, + style: _headerStyle, + ), + ), + Expanded( + flex: 2, + child: Text( + 'Awaiting Approval', + textAlign: TextAlign.center, + style: _headerStyle, + ), + ), + Expanded( + flex: 2, + child: Text( + 'Awaiting Policy', + textAlign: TextAlign.center, + style: _headerStyle, + ), + ), + Expanded( + flex: 2, + child: Text( + // stringFlag + " Issued", + 'Policy Created', textAlign: TextAlign.center, style: _headerStyle, ), ), - // (stringFlag == "Policies") - // ? Expanded( - // flex: 2, - // child: Text( - // "Total Premium Value", - // textAlign: TextAlign.center, - // style: _headerStyle, - // ), - // ) - // : const SizedBox.shrink(), ], ), ), @@ -1204,6 +1265,17 @@ class othersPendings extends StatelessWidget { // style: _tableDataStyle, // ), // ), + if (role == 'manager') ...[ + Expanded( + flex: 2, + child: Text( + row['handler_name'] ?? "", + + textAlign: TextAlign.center, + style: _tableDataStyle, + ), + ), + ], Expanded( flex: 2, child: Text( @@ -1217,25 +1289,48 @@ class othersPendings extends StatelessWidget { Expanded( flex: 2, child: Text( - issuedCount, + row['total_assigned'] ?? "", + // row['total_approval_pending'] ?? "", + textAlign: TextAlign.center, + style: _tableDataStyle, + ), + ), + Expanded( + flex: 2, + child: Text( + row['awaiting_quotation'] ?? "", + // row['total_approval_pending'] ?? "", + textAlign: TextAlign.center, + style: _tableDataStyle, + ), + ), + Expanded( + flex: 2, + child: Text( + row['pending_quotation_approval'] ?? "", + // row['total_approval_pending'] ?? "", + textAlign: TextAlign.center, + style: _tableDataStyle, + ), + ), + Expanded( + flex: 2, + child: Text( + row['awaiting_policy'] ?? "", + // row['total_approval_pending'] ?? "", + textAlign: TextAlign.center, + style: _tableDataStyle, + ), + ), + Expanded( + flex: 2, + child: Text( + row['policy_created'] ?? "", // row['total_approval_pending'] ?? "", textAlign: TextAlign.center, style: _tableDataStyle, ), ), - // stringFlag == "Policies" - // ? Expanded( - // flex: 2, - // child: Text( - // formatToCroresLakhsAndThousands(permiumValue), - // textAlign: TextAlign.center, - // style: GoogleFonts.inter( - // fontSize: 14, - // fontWeight: FontWeight.w500, - // ), - // ), - // ) - // : const SizedBox.shrink(), ], ), ); @@ -1553,12 +1648,14 @@ class UnassignedEnq extends StatelessWidget { final String title; final List> data; final VoidCallback? onRefresh; + String role; UnassignedEnq({ Key? key, required this.title, required this.data, this.onRefresh, + required this.role, }) : super(key: key); @override @@ -1647,26 +1744,28 @@ class UnassignedEnq extends StatelessWidget { // color: Colors.green , // width: 2, // ), - onTap: () { - // Print the id when row is clicked - print("Clicked ID: ${row['id']}"); - // You can also navigate or perform any action here + onTap: (role == 'handler') + ? () { + // Print the id when row is clicked + print("Clicked ID: ${row['id']}"); + // You can also navigate or perform any action here - showDialog( - context: context, - builder: (ctx) => AssignStaffDialog( - enquiryPrimaryId: row['id'], - regNum: row['reg_no'], - userId: 1, - onSubmit: (value) { - debugPrint("New assignY: $value"); - if (onRefresh != null) { - onRefresh!(); // ✅ call the parent's refresh - } - }, - ), - ); - }, + showDialog( + context: context, + builder: (ctx) => AssignStaffDialog( + enquiryPrimaryId: row['id'], + regNum: row['reg_no'], + userId: 1, + onSubmit: (value) { + debugPrint("New assignY: $value"); + if (onRefresh != null) { + onRefresh!(); // ✅ call the parent's refresh + } + }, + ), + ); + } + : null, child: Container( // height: 5200, margin: const EdgeInsets.symmetric(vertical: 5), diff --git a/lib/presentation/screens/handler/enquiryList.dart b/lib/presentation/screens/handler/enquiryList.dart new file mode 100644 index 0000000..313133e --- /dev/null +++ b/lib/presentation/screens/handler/enquiryList.dart @@ -0,0 +1,1212 @@ +// import 'dart:io' as html; +import 'package:http/http.dart' as http; +import 'package:dropdown_search/dropdown_search.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:go_router/go_router.dart'; +import 'package:google_fonts/google_fonts.dart'; +import 'package:intl/intl.dart'; +import 'package:shared_preferences/shared_preferences.dart'; + +import '../../../../core/routing/routes.dart'; + +import '../../../../core/services/api_service.dart'; +import 'package:universal_html/html.dart' as html; + +import '../../../../data/utils/Pagination.dart'; + +import '../../layouts/main_layout.dart'; +import '../../layouts/responsive_layout.dart'; +import '../../providers/manager_provider.dart'; +import '../../providers/quotation_staff_proivder.dart'; +import '../../providers/userRoleProvider.dart'; +import '../../themes/indicators/export_btn.dart'; +import '../../themes/indicators/search_field_theme.dart'; +import '../../widgets/custom_Stdate_EnDate_Filter.dart'; +import '../../widgets/custom_action_popup.dart'; +import '../staff/assignStaff.dart'; + +class EnquiryHandler extends ConsumerStatefulWidget { + const EnquiryHandler({super.key}); + @override + ConsumerState createState() => EnquiryHandlerState(); +} + +class EnquiryHandlerState extends ConsumerState { + int currentPage = 1; + int itemsPerPage = 10; + late ApiService apiService; + dynamic userId; + final _formKey = GlobalKey(); + + // List> dataVal = []; + List> getStaffData = []; + List> originalData = []; + List> filteredData = []; + bool isLoading = false; + dynamic roleId; + + Map controllers = {}; + List tabHeader = ['startDate', 'endDate']; + + @override + void initState() { + super.initState(); + + apiService = ApiService(); + + for (String field in tabHeader) { + controllers[field] = TextEditingController(); + } + + Future.microtask(() { + final id = ref.read(managerIdProvider); + roleId = ref.read(userRoleProvider); + userId = ref.read(userIdProvider); + + print("C72 => r : $roleId | mId: $id | uId: $userId "); + if (userId != null) { + getStaffList(userId, roleId); + } + }); + } + + void refresh() { + final id = ref.read(managerIdProvider); + final roleId = ref.read(userRoleProvider); + final userId = ref.read(userIdProvider); + + print("C82 => r : $roleId | mId: $id | uId: $userId "); + if (userId != null) { + getStaffList(userId, roleId); + } + } + + void filterDateRange() { + // ✅ Validate the form first + if (!_formKey.currentState!.validate()) { + // stop execution if validation fails + return; + } + + final fromDateText = controllers['startDate']?.text ?? ''; + final toDateText = controllers['endDate']?.text ?? ''; + + // Optional: double-check End >= Start + final fromDate = DateFormat('dd-MM-yyyy').parse(fromDateText); + final toDate = DateFormat('dd-MM-yyyy').parse(toDateText); + + if (toDate.isBefore(fromDate)) { + // This is already caught by the validator, but extra safety + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text("End Date cannot be earlier than Start Date")), + ); + return; + } + + // ✅ Call your API + getStaffList(userId, roleId); + } + + void refrshfilterDateRange() { + setState(() { + controllers['startDate']!.clear(); + controllers['endDate']!.clear(); + + controllers['startDate']?.text = ''; + controllers['endDate']?.text = ''; + // Reset the FormField validation + _formKey.currentState?.reset(); + }); + getStaffList(userId, roleId); + } + + Future getStaffList( + int managerId, + role, { + String fromDate = '', + String toDate = '', + }) async { + print('C89 => Fns called => $managerId | $role'); + setState(() { + isLoading = true; + }); + + try { + final response = await apiService.fetchPolicyList( + managerId, + role, + fromDate: controllers['startDate']?.text ?? '', + toDate: controllers['endDate']?.text ?? '', + ); + + print('FromDate : $fromDate'); + print('ToDate : $toDate'); + + if (response['status'] == 'success') { + final data = response['data']; + print('C99 => getStaffListData => ${response['data']}'); + final fromDate = response['from_date'] ?? ''; + final toDate = response['to_date'] ?? ''; + + print('FromDate : $fromDate'); + print('ToDate : $toDate'); + setState(() { + controllers['startDate']?.text = fromDate; + controllers['endDate']?.text = toDate; + + if (data is List) { + // Already a list of maps + getStaffData = List>.from(data); + } else if (data is Map) { + // Single object, wrap in a list + getStaffData = [Map.from(data)]; + } else { + getStaffData = []; + } + // getStaffData = List>.from(response['data']); + originalData = getStaffData; + filteredData = List.from(originalData); + // print('originalData - $getClaimPolicies'); + }); + } else { + getStaffData = []; + originalData = []; + } + } catch (e) { + print('Exception occurred: $e'); + } finally { + setState(() { + isLoading = false; + }); + } + } + + List get _paginatedData2 { + // Sort descending by id first + final sortedData = [...filteredData] + ..sort((a, b) => int.parse(b['id']) - int.parse(a['id'])); + + // final sortedData = [...filteredData]; + final startIndex = (currentPage - 1) * itemsPerPage; + final endIndex = (currentPage * itemsPerPage).clamp(0, sortedData.length); + + return sortedData.sublist(startIndex, endIndex); + } + + List get _paginatedData { + // Sort descending by id first + final sortedData = [...filteredData] + ..sort((a, b) => int.parse(b['id']) - int.parse(a['id'])); + + if (sortedData.isEmpty) return []; + + // Ensure currentPage is valid + final maxPage = (sortedData.length / itemsPerPage).ceil(); + final safePage = currentPage.clamp(1, maxPage); + + final startIndex = (safePage - 1) * itemsPerPage; + final endIndex = (startIndex + itemsPerPage).clamp(0, sortedData.length); + + return sortedData.sublist(startIndex, endIndex); + } + + void filterData(String query) { + print("FilterDAta - $query"); + setState(() { + filteredData = getStaffData.where((item) { + // final isActiveStatus = item['is_active'] == "1" ? "active" : "inactive"; + + return (item['agent_name'] ?? '-').toLowerCase().contains( + query.toLowerCase(), + ) || + (item['reg_no'] ?? '-').toLowerCase().contains( + query.toLowerCase(), + ) || + (item['insurer_name'] ?? '-').toLowerCase().contains( + query.toLowerCase(), + ) || + (item['assigned_to_name'] ?? '-').toLowerCase().contains( + query.toLowerCase(), + ) || + (item['insured_name'] ?? '-').toLowerCase().contains( + query.toLowerCase(), + ) || + (item['premium_amount'] ?? '-').toLowerCase().contains( + query.toLowerCase(), + ) || + (item['payment_mode'] ?? '-').toLowerCase().contains( + query.toLowerCase(), + ) || + (_formatDate(item['created_on']) ?? '-').toLowerCase().contains( + query.toLowerCase(), + ) || + (_formatDate(item['updated_on']) ?? '-').toLowerCase().contains( + query.toLowerCase(), + ) || + (item['policy_number'] ?? '-').toLowerCase().contains( + query.toLowerCase(), + ) || + (item['status'] ?? '-').toLowerCase().contains(query.toLowerCase()); + }).toList(); + }); + } + + final TextEditingController _searchStaffController = TextEditingController(); + + String _formatDate(String rawDate) { + try { + final dateTime = DateTime.parse(rawDate); + return DateFormat( + 'dd-MM-yyyy HH:mm', + ).format(dateTime); // 24-hour format + } catch (e) { + return rawDate; // fallback if parsing fails + } + } + + List _buildPopupMenuActions( + BuildContext context, + dynamic data, + id, + regNum, + ) { + return [ + if (roleId == 'manager' && data['status'] == 'Awaiting Quotation') ...[ + Material( + color: Colors.transparent, + child: InkWell( + onTap: () { + Navigator.pop(context); + showDialog( + context: context, + builder: (ctx) => AssignStaffDialog( + enquiryPrimaryId: id, + regNum: regNum, + userId: 1, + onSubmit: (value) { + debugPrint("New assignY: $value"); + refresh(); + }, + ), + ); + }, + hoverColor: Color(0xFFE3F1F0), + splashColor: Color(0xFFE3F1F0), + borderRadius: BorderRadius.circular(6), + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4), + child: Row( + // mainAxisSize: MainAxisSize.min, + children: [ + Image.asset( + "assets/miscellaneous/image_3.png", + // height: 45, + // width: 15, + ), + + SizedBox(width: 10), + Text('Assign Staff'), + ], + ), + ), + ), + ), + ], + + Material( + color: Colors.transparent, + child: InkWell( + onTap: () async { + Navigator.pop(context); + final prefs = await SharedPreferences.getInstance(); + + // ✅ Remove old value (if any) + await prefs.remove('enqStaffDataId'); + + // ✅ Save the new id + await prefs.setString('enqStaffDataId', id.toString()); + + // Read it back if needed + // final dynamic? enqStaffDataId = prefs.getString('enqStaffDataId'); + + // ✅ Update provider too + // ref.read(quotationStaffIdProvider.notifier).state = enqStaffDataId; + ref.read(quotationStaffIdProvider.notifier).state = id; + + context.go(AppRoutes.quotation); + }, + hoverColor: Color(0xFFE3F1F0), + splashColor: Color(0xFFE3F1F0), + borderRadius: BorderRadius.circular(6), + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4), + child: Row( + // mainAxisSize: MainAxisSize.min, + children: [ + Image.asset( + "assets/miscellaneous/image_1.png", + height: 15, + width: 15, + ), + + const SizedBox(width: 10), + ((data['status'] == 'Policy Created') || + (data['status'] == 'Quotation Accepted')) + ? const Text('View Quotation') + : const Text('Create Quotation'), + ], + ), + ), + ), + ), + + if (data['status'] == 'Quotation Accepted' || + data['status'] == 'Policy Created') + Material( + color: Colors.transparent, + child: InkWell( + onTap: () async { + Navigator.pop(context); + + final prefs = await SharedPreferences.getInstance(); + + // ✅ Remove old value (if any) + await prefs.remove('enqStaffDataId'); + + // ✅ Save the new id + await prefs.setString('enqStaffDataId', id.toString()); + + // ✅ Update provider too + + ref.read(quotationStaffIdProvider.notifier).state = id; + context.go(AppRoutes.policy); + }, + hoverColor: Color(0xFFE3F1F0), + splashColor: Color(0xFFE3F1F0), + borderRadius: BorderRadius.circular(6), + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4), + child: Row( + // mainAxisSize: MainAxisSize.min, + children: [ + Image.asset( + "assets/miscellaneous/image_2.png", + height: 15, + width: 15, + ), + + const SizedBox(width: 10), + data['status'] == 'Policy Created' + ? Text('View Policy') + : Text('Create Policy'), + ], + ), + ), + ), + ), + ]; + } + + @override + Widget build(BuildContext context) { + return MainLayout( + title: "Enquiries", + body: Container( + // color: Colors.yellow.shade50, + width: MediaQuery.of(context).size.width, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Container( + height: 40, + 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), + onPressed: () { + context.go(AppRoutes.dashboard); + }, + splashRadius: 28, + hoverColor: Colors.black12, + padding: const EdgeInsets.all(8), + constraints: const BoxConstraints(), + ), + ), + const SizedBox(width: 5), // spacing between icon and text + Text( + "Enquiries", + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.w600, + ), + ), + ], + ), + ), + ), + SizedBox(height: 10), + ResponsiveLayout.isMobile(context) + ? Container( + height: MediaQuery.of(context).size.height * 0.69, + child: SingleChildScrollView( + child: Padding( + padding: EdgeInsets.all(8), + child: _buildContent(context), + ), + ), + ) + : Expanded( + child: Container( + width: MediaQuery.of(context).size.width, + padding: EdgeInsets.all(8.0), + child: _buildContent(context), + ), + ), + + // Expanded( + // child: Container( + // // color: Colors.green, + // // color: Colors.green.shade50, + // width: MediaQuery.of(context).size.width, + // + // padding: EdgeInsets.all(8.0), + // child: + // ), + // ), + Container( + // height: 20, + width: MediaQuery.of(context).size.width, + // color: Colors.green.shade50, + child: PaginationControls( + currentPage: currentPage, + itemsPerPage: itemsPerPage, + // totalItems: dataVal.length, + totalItems: filteredData.length, + // activeColor: layoutColor, // your theme color + onPageChanged: (page) { + setState(() { + currentPage = page; + }); + }, + onItemsPerPageChanged: (items) { + setState(() { + itemsPerPage = items; + currentPage = 1; + }); + }, + ), + ), + ], + ), + ), + ); + } + + Widget _buildContent(BuildContext context) { + return Column( + children: [ + if (ResponsiveLayout.isMobile(context)) ...[ + Container( + padding: EdgeInsets.all(8.0), + color: Color(0xffD9EBE8), + child: DateFilterRow( + startController: controllers['startDate']!, + endController: controllers['endDate']!, + formKey: _formKey, + isMobile: ResponsiveLayout.isMobile(context), + onFilter: () { + // call your filter logic + filterDateRange(); + }, + onRefresh: () { + // call your refresh logic + refrshfilterDateRange(); + }, + ), + ), + ], + + Container( + // height: 40, + // color: Colors.pink, + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + if (!ResponsiveLayout.isMobile(context)) ...[ + DateFilterRow( + startController: controllers['startDate']!, + endController: controllers['endDate']!, + 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(), + ], + + ThemedSearchField( + hintText: 'Search', + backgroundColor: Color(0xFFF6F8F8), + onChanged: filterData, + controller: _searchStaffController, + txtwidth: ResponsiveLayout.isMobile(context) + ? MediaQuery.of(context).size.width * 0.7 + : MediaQuery.of(context).size.width * 0.2, + ), + + ResponsiveLayout.isMobile(context) + ? Spacer() + : SizedBox(width: 10), + + ExportBtn( + sheetName: "Enquiry", + fileName: "Enquiry_list", + data: filteredData, + txt: !ResponsiveLayout.isMobile(context) ? true : false, + headers: [ + "created_on", + "updated_on", + "reg_no", + "agent_name", + "assigned_to_name", + "insurer_name", + "insured_name", + "premium_amount", + "payment_mode", + "policy_number", + "status", + ], + ), + SizedBox(width: 10), + GestureDetector( + onTap: () async { + final prefs = await SharedPreferences.getInstance(); + + await prefs.remove('enqAgentDataId'); + context.go(AppRoutes.tabEnquiry); + // print('Export'); + }, + child: Container( + padding: EdgeInsets.all(8.0), + decoration: BoxDecoration( + color: Color(0xFF425B5B), + borderRadius: BorderRadius.circular(8.0), + ), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Icon(Icons.add, color: Colors.white), + + if (!ResponsiveLayout.isMobile(context)) ...[ + SizedBox(width: 10), + Text( + 'Raise Enquiry', + style: GoogleFonts.inter( + color: Colors.white, + fontWeight: FontWeight.w600, + fontSize: 14, + ), + ), + // GestureDetector( + // onTap: () { + // ref + // .read( + // enquiryIdProvider.notifier, + // ) + // .state = + // null; + // context.go(AppRoutes.tabEnquiry); + // }, + // child: Text( + // 'Create New Enquiry', + // style: GoogleFonts.inter( + // color: Colors.white, + // fontWeight: FontWeight.w600, + // fontSize: 14, + // ), + // ), + // ), + ], + ], + ), + ), + ), + // SizedBox(width: 10), + // GestureDetector( + // onTap: () { + // // print('Export'); + // }, + // child: Container( + // padding: EdgeInsets.all(8.0), + // decoration: BoxDecoration( + // color: Color(0xFF425B5B), + // borderRadius: BorderRadius.circular(8.0), + // ), + // child: Row( + // mainAxisSize: MainAxisSize.min, + // children: [ + // Icon(Icons.add, color: Colors.white), + // + // if (!ResponsiveLayout.isMobile(context)) ...[ + // SizedBox(width: 10), + // + // GestureDetector( + // onTap: () { + // // context.go(AppRoutes.agent / create); + // context.go('/staff/create'); + // }, + // child: Text( + // 'Create New Staff', + // style: TextStyle( + // color: Colors.white, + // fontWeight: FontWeight.w600, + // fontSize: 14, + // ), + // ), + // ), + // ], + // ], + // ), + // ), + // ), + ], + ), + ), + SizedBox(height: 10), + if (!ResponsiveLayout.isMobile(context)) + Container( + decoration: BoxDecoration( + color: Color(0xFFEDF6F5), + borderRadius: BorderRadius.circular(6), + ), + padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 16), + child: const Row( + children: [ + Expanded( + flex: 3, + child: Text('Created Date', style: _headerStyle), + ), + Expanded( + flex: 3, + child: Text('Updated Date', style: _headerStyle), + ), + Expanded(flex: 2, child: Text('Reg.No', style: _headerStyle)), + Expanded(flex: 2, child: Text('Partner', style: _headerStyle)), + Expanded( + flex: 2, + child: Text('Assigned To', style: _headerStyle), + ), + Expanded(flex: 3, child: Text('Insurer', style: _headerStyle)), + Expanded( + flex: 3, + child: Padding( + padding: EdgeInsets.only(left: 8.0), + child: Text('Insured Name', style: _headerStyle), + ), + ), + Expanded(flex: 2, child: Text('Premium', style: _headerStyle)), + Expanded( + flex: 2, + child: Text('Payment Mode', style: _headerStyle), + ), + Expanded( + flex: 2, + child: Text('Policy Number', style: _headerStyle), + ), + + Expanded(flex: 3, child: Text('Status', style: _headerStyle)), + Expanded(flex: 1, child: Text('Action', style: _headerStyle)), + ], + ), + ), + ResponsiveLayout.isMobile(context) + ? _buildDataTable(context) + : Expanded(child: _buildDataTable(context)), + ], + ); + } + + Widget _buildDataTable(BuildContext context) { + if (filteredData.isEmpty) { + return const SizedBox( + height: 50, + child: Center(child: Text('No available data')), + ); + } + + final sortedData = [..._paginatedData]; + + if (ResponsiveLayout.isMobile(context)) { + // Use Column instead of ListView + return Column( + children: List.generate(sortedData.length, (index) { + final startIndex = (currentPage - 1) * itemsPerPage; + final item = sortedData[index]; + final sno = startIndex + index; + return _buildDataCard(item, sno); + }), + ); + } + + // Desktop: keep ListView.builder + return ListView.builder( + itemCount: sortedData.length + 1, + itemBuilder: (context, index) { + if (index == 0) return _buildHeader(); + final startIndex = (currentPage - 1) * itemsPerPage; + final item = sortedData[index - 1]; + final sno = startIndex + index; + return _buildDataRow(item, sno); + }, + ); + } + + // Widget _buildDataTable(BuildContext context) { + // if (filteredData.isEmpty) { + // return const SizedBox( + // height: 50, + // child: Center(child: Text('No available data')), + // ); + // } + // + // final sortedData = [..._paginatedData]; + // return ListView.builder( + // itemCount: ResponsiveLayout.isMobile(context) + // ? sortedData + // .length // only cards for mobile + // : sortedData.length + 1, // +1 for header in desktop + // itemBuilder: (context, index) { + // if (!ResponsiveLayout.isMobile(context) && index == 0) { + // return _buildHeader(); + // } + // + // final startIndex = (currentPage - 1) * itemsPerPage; + // final item = + // sortedData[index - (ResponsiveLayout.isMobile(context) ? 0 : 1)]; + // final sno = startIndex + index; + // + // return !ResponsiveLayout.isMobile(context) + // ? _buildDataRow(item, sno) + // : _buildDataCard(item, sno); + // }, + // ); + // } + + Widget _buildHeader() { + return SizedBox.shrink(); + } + + Widget _buildDataRow(Map item, sno) { + return Container( + padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 16), + // margin: const EdgeInsets.only(top: 10), + decoration: BoxDecoration( + color: Colors.white, + // color: Color(0xFFE0F7F9), + border: const Border( + bottom: BorderSide(color: Color(0xFFEAEAEA), width: 1), + ), + borderRadius: BorderRadius.circular(8), + ), + child: Row( + children: [ + Expanded( + flex: 3, + child: Text( + _formatDate(item['created_on']) ?? '-', + style: _dataBold, + softWrap: true, + maxLines: 3, + ), + ), + Expanded( + flex: 3, + child: Text( + _formatDate(item['updated_on']) ?? '-', + style: _dataBold, + softWrap: true, + maxLines: 3, + ), + ), + Expanded( + flex: 2, + child: Text(item['reg_no'] ?? '-', style: _dataBold), + ), + Expanded( + flex: 2, + child: Text( + _formatDate(item['agent_name']) ?? '-', + style: _dataBold, + ), + ), + Expanded( + flex: 2, + child: Text(item['assigned_to_name'] ?? '-', style: _dataBold), + ), + Expanded( + flex: 3, + child: Text( + item['insurer_name'] ?? '-', + style: _dataBold, + softWrap: true, + maxLines: 3, + ), + ), + Expanded( + flex: 3, + child: Center( + child: Text( + item['insured_name'] ?? '-', + style: _dataBold, + softWrap: true, + maxLines: 3, + ), + ), + ), + Expanded( + flex: 2, + child: Text( + item['premium_amount'] ?? '-', + style: _dataBold, + softWrap: true, + maxLines: 3, + ), + ), + Expanded( + flex: 2, + child: Text( + item['payment_mode'] ?? '-', + style: _dataBold, + softWrap: true, + maxLines: 3, + ), + ), + Expanded( + flex: 2, + child: Text( + item['policy_number'] ?? '-', + style: _dataBold, + softWrap: true, + maxLines: 3, + ), + ), + + Expanded( + flex: 3, + child: Text(item['status'] ?? '-', style: _dataBold), + ), + + Expanded( + flex: 1, + child: Row( + children: [ + PopupMenuButton( + color: Colors.white, + padding: EdgeInsets.zero, + offset: Offset(0, 30), + icon: Icon( + Icons.more_vert, + color: Color(0xFF475569), + size: 14, + ), + itemBuilder: (context) => [ + CustomPopupMenuEntry( + child: Container( + padding: EdgeInsets.symmetric( + horizontal: 8, + vertical: 8, + ), + child: Column( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: _buildPopupMenuActions( + context, + item, + item['id'], + item['reg_no'], + ), + ), + ), + ), + ], + ), + ], + ), + ), + ], + ), + ); + } + + Widget _buildDataCard(Map item, int sno) { + return Container( + margin: const EdgeInsets.symmetric(vertical: 6, horizontal: 8), + padding: const EdgeInsets.all(12), + decoration: BoxDecoration( + color: const Color(0xFFF6FEFD), + borderRadius: BorderRadius.circular(8.0), + border: Border.all(color: const Color(0xffD9EBE8)), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + /// Top row: Reg.No + Action menu + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text(item['reg_no'] ?? '-', style: _headerStyle), + PopupMenuButton( + color: Colors.white, + padding: EdgeInsets.zero, + offset: Offset(0, 30), + icon: Icon(Icons.more_vert, color: Color(0xFF475569), size: 14), + itemBuilder: (context) => [ + CustomPopupMenuEntry( + child: Container( + padding: EdgeInsets.symmetric(horizontal: 8, vertical: 8), + child: Column( + mainAxisSize: MainAxisSize.min, + children: _buildPopupMenuActions( + context, + item, + item['id'], + item['reg_no'], + ), + ), + ), + ), + ], + ), + ], + ), + const Divider(color: Color(0xffD9EBE8), thickness: 0.8), + + /// Company + Status + Row( + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text("Company", style: _cardheaderStyle), + Text(item['insurer_name'] ?? '-', style: _cardBodyStyle), + ], + ), + ), + const SizedBox(width: 5), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text("Status", style: _cardheaderStyle), + Text(item['status'] ?? '-', style: _cardBodyStyle), + ], + ), + ), + ], + ), + const SizedBox(height: 15), + + /// Agents + Assigned To + Row( + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text("Partner", style: _cardheaderStyle), + Text(item['agent_name'] ?? '-', style: _cardBodyStyle), + ], + ), + ), + const SizedBox(width: 5), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text("Assigned To", style: _cardheaderStyle), + Text( + item['assigned_to_name'] ?? '-', + style: _cardBodyStyle, + ), + ], + ), + ), + ], + ), + const SizedBox(height: 15), + + /// Insured Name + Premium + Row( + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text("Insured Name", style: _cardheaderStyle), + Text(item['insured_name'] ?? '-', style: _cardBodyStyle), + ], + ), + ), + const SizedBox(width: 5), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text("Premium", style: _cardheaderStyle), + Text(item['premium_amount'] ?? '-', style: _cardBodyStyle), + ], + ), + ), + ], + ), + const SizedBox(height: 15), + + /// Payment Mode + Policy Number + Row( + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text("Payment Mode", style: _cardheaderStyle), + Text(item['payment_mode'] ?? '-', style: _cardBodyStyle), + ], + ), + ), + const SizedBox(width: 5), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text("Policy Number", style: _cardheaderStyle), + Text(item['policy_number'] ?? '-', style: _cardBodyStyle), + ], + ), + ), + ], + ), + const SizedBox(height: 15), + + /// Date + Remarks + Row( + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text("Date", style: _cardheaderStyle), + Text( + _formatDate(item['updated_on']) ?? '-', + style: _cardBodyStyle, + ), + ], + ), + ), + const SizedBox(width: 5), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text("Remarks", style: _cardheaderStyle), + Text( + item['remarks'] ?? '-', + style: _cardBodyStyle, + maxLines: 3, + ), + ], + ), + ), + ], + ), + ], + ), + ); + } + + static final _dataBold = TextStyle( + fontSize: 14, + + fontWeight: FontWeight.w400, + color: Color(0xFF000000), + ); + + static final _dataSub = TextStyle( + fontSize: 10, + fontWeight: FontWeight.w300, + color: Color(0xFF585757), + ); + + static const _headerStyle = TextStyle( + color: Colors.black, + fontWeight: FontWeight.bold, + ); + static const _cardheaderStyle = TextStyle( + color: Colors.black, + fontWeight: FontWeight.w600, + fontSize: 12, + ); + + static const _cardBodyStyle = TextStyle( + color: Color(0xFF545454), + fontWeight: FontWeight.w400, + fontSize: 12, + ); +} diff --git a/lib/presentation/screens/login/login_screen.dart b/lib/presentation/screens/login/login_screen.dart index 73bd9e4..dbe40fa 100644 --- a/lib/presentation/screens/login/login_screen.dart +++ b/lib/presentation/screens/login/login_screen.dart @@ -136,6 +136,8 @@ class _LoginScreenState extends ConsumerState { if (roleId.toString() == "1") { role = "manager"; } else if (roleId.toString() == "2") { + role = "handler"; + } else if (roleId.toString() == "3") { role = "staff"; } else { role = "agent"; // fallback if unexpected value @@ -171,6 +173,20 @@ class _LoginScreenState extends ConsumerState { print("⚠️ Manager ID is null or invalid"); } + // Handler ID + final dynamic handlerIdRaw = data['manager_id']; + final int? handlerId = handlerIdRaw is int + ? handlerIdRaw + : int.tryParse(handlerIdRaw.toString()); + + if (handlerId != null) { + ref.read(handlerIdProvider.notifier).state = handlerId; + print("✅ handlerId ID saved globally: $handlerId"); + await prefs.setInt('handlerId', handlerId); + } else { + print("⚠️ handlerId ID is null or invalid"); + } + // User ID final dynamic userIdRaw = data['id']; final int? userId = userIdRaw is int @@ -966,7 +982,7 @@ class _LoginScreenState extends ConsumerState { width: double.infinity, height: 50, child: CommonButton( - text: "Login as Agent", + text: "Login as Partner", backgroundColor: Color(0xFF436462), onPressed: () async { setState(() { diff --git a/lib/presentation/screens/staff/Enquiry/enquiry_list.dart b/lib/presentation/screens/staff/Enquiry/enquiry_list.dart index 937f871..483d486 100644 --- a/lib/presentation/screens/staff/Enquiry/enquiry_list.dart +++ b/lib/presentation/screens/staff/Enquiry/enquiry_list.dart @@ -287,48 +287,47 @@ class EnquiryStaffState extends ConsumerState { regNum, ) { return [ - if (roleId == 'manager' && data['status'] == 'Awaiting Quotation') ...[ - Material( - color: Colors.transparent, - child: InkWell( - onTap: () { - Navigator.pop(context); - showDialog( - context: context, - builder: (ctx) => AssignStaffDialog( - enquiryPrimaryId: id, - regNum: regNum, - userId: 1, - onSubmit: (value) { - debugPrint("New assignY: $value"); - refresh(); - }, - ), - ); - }, - hoverColor: Color(0xFFE3F1F0), - splashColor: Color(0xFFE3F1F0), - borderRadius: BorderRadius.circular(6), - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4), - child: Row( - // mainAxisSize: MainAxisSize.min, - children: [ - Image.asset( - "assets/miscellaneous/image_3.png", - // height: 45, - // width: 15, - ), - - SizedBox(width: 10), - Text('Assign Staff'), - ], - ), - ), - ), - ), - ], - + // if (roleId == 'manager' && data['status'] == 'Awaiting Quotation') ...[ + // Material( + // color: Colors.transparent, + // child: InkWell( + // onTap: () { + // Navigator.pop(context); + // showDialog( + // context: context, + // builder: (ctx) => AssignStaffDialog( + // enquiryPrimaryId: id, + // regNum: regNum, + // userId: 1, + // onSubmit: (value) { + // debugPrint("New assignY: $value"); + // refresh(); + // }, + // ), + // ); + // }, + // hoverColor: Color(0xFFE3F1F0), + // splashColor: Color(0xFFE3F1F0), + // borderRadius: BorderRadius.circular(6), + // child: Padding( + // padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4), + // child: Row( + // // mainAxisSize: MainAxisSize.min, + // children: [ + // Image.asset( + // "assets/miscellaneous/image_3.png", + // // height: 45, + // // width: 15, + // ), + // + // SizedBox(width: 10), + // Text('Assign Staff'), + // ], + // ), + // ), + // ), + // ), + // ], Material( color: Colors.transparent, child: InkWell( @@ -435,7 +434,7 @@ class EnquiryStaffState extends ConsumerState { mainAxisAlignment: MainAxisAlignment.start, children: [ Container( - height: 40, + height: 30, width: MediaQuery.of(context).size.width, child: GestureDetector( onTap: () { @@ -470,7 +469,7 @@ class EnquiryStaffState extends ConsumerState { ), ), ), - SizedBox(height: 10), + SizedBox(height: 5), ResponsiveLayout.isMobile(context) ? Container( height: MediaQuery.of(context).size.height * 0.69, @@ -647,49 +646,10 @@ class EnquiryStaffState extends ConsumerState { "status", ], ), - - // SizedBox(width: 10), - // GestureDetector( - // onTap: () { - // // print('Export'); - // }, - // child: Container( - // padding: EdgeInsets.all(8.0), - // decoration: BoxDecoration( - // color: Color(0xFF425B5B), - // borderRadius: BorderRadius.circular(8.0), - // ), - // child: Row( - // mainAxisSize: MainAxisSize.min, - // children: [ - // Icon(Icons.add, color: Colors.white), - // - // if (!ResponsiveLayout.isMobile(context)) ...[ - // SizedBox(width: 10), - // - // GestureDetector( - // onTap: () { - // // context.go(AppRoutes.agent / create); - // context.go('/staff/create'); - // }, - // child: Text( - // 'Create New Staff', - // style: TextStyle( - // color: Colors.white, - // fontWeight: FontWeight.w600, - // fontSize: 14, - // ), - // ), - // ), - // ], - // ], - // ), - // ), - // ), ], ), ), - SizedBox(height: 10), + // SizedBox(height: 10), if (!ResponsiveLayout.isMobile(context)) Container( decoration: BoxDecoration( @@ -701,38 +661,23 @@ class EnquiryStaffState extends ConsumerState { children: [ Expanded( flex: 3, - child: Text('Created Date', style: _headerStyle), + child: Text('Received Date & Time', style: _headerStyle), + ), + Expanded(flex: 2, child: Text('Partner ', style: _headerStyle)), + Expanded(flex: 4, child: Text('Insurer', style: _headerStyle)), + Expanded( + flex: 3, + child: Text('Vehicle No', style: _headerStyle), ), Expanded( flex: 3, - child: Text('Updated Date', style: _headerStyle), + child: Text('Insured Name', style: _headerStyle), ), - Expanded(flex: 2, child: Text('Reg.No', style: _headerStyle)), - Expanded(flex: 2, child: Text('Partner', style: _headerStyle)), - Expanded( - flex: 2, - child: Text('Assigned To', style: _headerStyle), - ), - Expanded(flex: 3, child: Text('Insurer', style: _headerStyle)), Expanded( flex: 3, - child: Padding( - padding: EdgeInsets.only(left: 8.0), - child: Text('Insured Name', style: _headerStyle), - ), + child: Text('Assigned Date & Time', style: _headerStyle), ), - Expanded(flex: 2, child: Text('Premium', style: _headerStyle)), - Expanded( - flex: 2, - child: Text('Payment Mode', style: _headerStyle), - ), - Expanded( - flex: 2, - child: Text('Policy Number', style: _headerStyle), - ), - Expanded(flex: 3, child: Text('Status', style: _headerStyle)), - Expanded(flex: 1, child: Text('Action', style: _headerStyle)), ], ), ), @@ -836,6 +781,51 @@ class EnquiryStaffState extends ConsumerState { maxLines: 3, ), ), + + Expanded( + flex: 2, + child: Text( + item['agent_name'] ?? '-', + style: _dataBold, + softWrap: true, + maxLines: 2, + ), + ), + Expanded( + flex: 4, + child: Text( + item['insurer_name'] ?? '-', + style: _dataBold, + softWrap: true, + maxLines: 3, + ), + ), + + Expanded( + flex: 3, + child: InkWell( + hoverColor: Color(0xffD9EBE8), + focusColor: Color(0xffD9EBE8), + splashColor: Color(0xffD9EBE8), + highlightColor: Color(0xffD9EBE8), + onTap: () { + dynamic id = item['id']; + print("ENQID : $id "); + }, + child: Text(item['reg_no'] ?? '-', style: _dataBold), + ), + ), + Expanded( + flex: 3, + + child: Text( + item['insured_name'] ?? '-', + style: _dataBold, + softWrap: true, + maxLines: 3, + ), + ), + Expanded( flex: 3, child: Text( @@ -845,112 +835,81 @@ class EnquiryStaffState extends ConsumerState { maxLines: 3, ), ), - Expanded( - flex: 2, - child: Text(item['reg_no'] ?? '-', style: _dataBold), - ), - Expanded( - flex: 2, - child: Text( - _formatDate(item['agent_name']) ?? '-', - style: _dataBold, - ), - ), - Expanded( - flex: 2, - child: Text(item['assigned_to_name'] ?? '-', style: _dataBold), - ), - Expanded( - flex: 3, - child: Text( - item['insurer_name'] ?? '-', - style: _dataBold, - softWrap: true, - maxLines: 3, - ), - ), - Expanded( - flex: 3, - child: Center( - child: Text( - item['insured_name'] ?? '-', - style: _dataBold, - softWrap: true, - maxLines: 3, - ), - ), - ), - Expanded( - flex: 2, - child: Text( - item['premium_amount'] ?? '-', - style: _dataBold, - softWrap: true, - maxLines: 3, - ), - ), - Expanded( - flex: 2, - child: Text( - item['payment_mode'] ?? '-', - style: _dataBold, - softWrap: true, - maxLines: 3, - ), - ), - Expanded( - flex: 2, - child: Text( - item['policy_number'] ?? '-', - style: _dataBold, - softWrap: true, - maxLines: 3, - ), - ), - Expanded( flex: 3, child: Text(item['status'] ?? '-', style: _dataBold), ), - Expanded( - flex: 1, - child: Row( - children: [ - PopupMenuButton( - color: Colors.white, - padding: EdgeInsets.zero, - offset: Offset(0, 30), - icon: Icon( - Icons.more_vert, - color: Color(0xFF475569), - size: 14, - ), - itemBuilder: (context) => [ - CustomPopupMenuEntry( - child: Container( - padding: EdgeInsets.symmetric( - horizontal: 8, - vertical: 8, - ), - child: Column( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, - children: _buildPopupMenuActions( - context, - item, - item['id'], - item['reg_no'], - ), - ), - ), - ), - ], - ), - ], - ), - ), + // Expanded( + // flex: 2, + // child: Text(item['assigned_to_name'] ?? '-', style: _dataBold), + // ), + // + // Expanded( + // flex: 2, + // child: Text( + // item['premium_amount'] ?? '-', + // style: _dataBold, + // softWrap: true, + // maxLines: 3, + // ), + // ), + // Expanded( + // flex: 2, + // child: Text( + // item['payment_mode'] ?? '-', + // style: _dataBold, + // softWrap: true, + // maxLines: 3, + // ), + // ), + // Expanded( + // flex: 2, + // child: Text( + // item['policy_number'] ?? '-', + // style: _dataBold, + // softWrap: true, + // maxLines: 3, + // ), + // ), + // Expanded( + // flex: 1, + // child: Row( + // children: [ + // PopupMenuButton( + // color: Colors.white, + // padding: EdgeInsets.zero, + // offset: Offset(0, 30), + // icon: Icon( + // Icons.more_vert, + // color: Color(0xFF475569), + // size: 14, + // ), + // itemBuilder: (context) => [ + // CustomPopupMenuEntry( + // child: Container( + // padding: EdgeInsets.symmetric( + // horizontal: 8, + // vertical: 8, + // ), + // child: Column( + // mainAxisSize: MainAxisSize.min, + // mainAxisAlignment: MainAxisAlignment.start, + // crossAxisAlignment: CrossAxisAlignment.start, + // children: _buildPopupMenuActions( + // context, + // item, + // item['id'], + // item['reg_no'], + // ), + // ), + // ), + // ), + // ], + // ), + // ], + // ), + // ), ], ), ); diff --git a/lib/presentation/screens/staff/Enquiry/enquiry_list_old.dart b/lib/presentation/screens/staff/Enquiry/enquiry_list_old.dart new file mode 100644 index 0000000..e7f9b2b --- /dev/null +++ b/lib/presentation/screens/staff/Enquiry/enquiry_list_old.dart @@ -0,0 +1,1172 @@ +// import 'dart:io' as html; +import 'package:http/http.dart' as http; +import 'package:dropdown_search/dropdown_search.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:go_router/go_router.dart'; +import 'package:google_fonts/google_fonts.dart'; +import 'package:intl/intl.dart'; +import 'package:shared_preferences/shared_preferences.dart'; + +import '../../../../core/config/env.dart'; +import '../../../../core/routing/routes.dart'; +import '../../../../core/services/api_service.dart'; +import '../../../../data/services/auth_service.dart'; + +import 'package:toastification/toastification.dart'; +import 'package:universal_html/html.dart' as html; + +import '../../../../core/routing/routes.dart'; +import '../../../../core/services/api_service.dart'; +import '../../../../data/services/auth_service.dart'; +import '../../../../data/utils/Pagination.dart'; +import '../../../../data/utils/toastNotification.dart'; +import '../../../../data/utils/validators.dart'; + +import '../../../layouts/main_layout.dart'; +import '../../../layouts/responsive_layout.dart'; +import '../../../providers/manager_provider.dart'; +import '../../../providers/quotation_staff_proivder.dart'; +import '../../../providers/userRoleProvider.dart'; +import '../../../themes/indicators/customizd_file_upload.dart'; +import '../../../themes/indicators/date_field_theme.dart'; +import '../../../themes/indicators/export_btn.dart'; +import '../../../themes/indicators/search_field_theme.dart'; +import '../../../themes/indicators/text_field_theme.dart'; + +import 'package:file_picker/file_picker.dart'; + +import '../../../widgets/custom_Stdate_EnDate_Filter.dart'; +import '../../../widgets/custom_action_popup.dart'; +import '../assignStaff.dart'; + +class EnquiryStaff extends ConsumerStatefulWidget { + const EnquiryStaff({super.key}); + @override + ConsumerState createState() => EnquiryStaffState(); +} + +class EnquiryStaffState extends ConsumerState { + int currentPage = 1; + int itemsPerPage = 10; + late ApiService apiService; + dynamic userId; + final _formKey = GlobalKey(); + + // List> dataVal = []; + List> getStaffData = []; + List> originalData = []; + List> filteredData = []; + bool isLoading = false; + dynamic roleId; + + Map controllers = {}; + List tabHeader = ['startDate', 'endDate']; + + @override + void initState() { + super.initState(); + + apiService = ApiService(); + + for (String field in tabHeader) { + controllers[field] = TextEditingController(); + } + + Future.microtask(() { + final id = ref.read(managerIdProvider); + roleId = ref.read(userRoleProvider); + userId = ref.read(userIdProvider); + + print("C72 => r : $roleId | mId: $id | uId: $userId "); + if (userId != null) { + getStaffList(userId, roleId); + } + }); + } + + void refresh() { + final id = ref.read(managerIdProvider); + final roleId = ref.read(userRoleProvider); + final userId = ref.read(userIdProvider); + + print("C82 => r : $roleId | mId: $id | uId: $userId "); + if (userId != null) { + getStaffList(userId, roleId); + } + } + + void filterDateRange() { + // ✅ Validate the form first + if (!_formKey.currentState!.validate()) { + // stop execution if validation fails + return; + } + + final fromDateText = controllers['startDate']?.text ?? ''; + final toDateText = controllers['endDate']?.text ?? ''; + + // Optional: double-check End >= Start + final fromDate = DateFormat('dd-MM-yyyy').parse(fromDateText); + final toDate = DateFormat('dd-MM-yyyy').parse(toDateText); + + if (toDate.isBefore(fromDate)) { + // This is already caught by the validator, but extra safety + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text("End Date cannot be earlier than Start Date")), + ); + return; + } + + // ✅ Call your API + getStaffList(userId, roleId); + } + + void refrshfilterDateRange() { + setState(() { + controllers['startDate']!.clear(); + controllers['endDate']!.clear(); + + controllers['startDate']?.text = ''; + controllers['endDate']?.text = ''; + // Reset the FormField validation + _formKey.currentState?.reset(); + }); + getStaffList(userId, roleId); + } + + Future getStaffList( + int managerId, + role, { + String fromDate = '', + String toDate = '', + }) async { + print('C89 => Fns called => $managerId | $role'); + setState(() { + isLoading = true; + }); + + try { + final response = await apiService.fetchPolicyList( + managerId, + role, + fromDate: controllers['startDate']?.text ?? '', + toDate: controllers['endDate']?.text ?? '', + ); + + print('FromDate : $fromDate'); + print('ToDate : $toDate'); + + if (response['status'] == 'success') { + final data = response['data']; + print('C99 => getStaffListData => ${response['data']}'); + final fromDate = response['from_date'] ?? ''; + final toDate = response['to_date'] ?? ''; + + print('FromDate : $fromDate'); + print('ToDate : $toDate'); + setState(() { + controllers['startDate']?.text = fromDate; + controllers['endDate']?.text = toDate; + + if (data is List) { + // Already a list of maps + getStaffData = List>.from(data); + } else if (data is Map) { + // Single object, wrap in a list + getStaffData = [Map.from(data)]; + } else { + getStaffData = []; + } + // getStaffData = List>.from(response['data']); + originalData = getStaffData; + filteredData = List.from(originalData); + // print('originalData - $getClaimPolicies'); + }); + } else { + getStaffData = []; + originalData = []; + } + } catch (e) { + print('Exception occurred: $e'); + } finally { + setState(() { + isLoading = false; + }); + } + } + + List get _paginatedData2 { + // Sort descending by id first + final sortedData = [...filteredData] + ..sort((a, b) => int.parse(b['id']) - int.parse(a['id'])); + + // final sortedData = [...filteredData]; + final startIndex = (currentPage - 1) * itemsPerPage; + final endIndex = (currentPage * itemsPerPage).clamp(0, sortedData.length); + + return sortedData.sublist(startIndex, endIndex); + } + + List get _paginatedData { + // Sort descending by id first + final sortedData = [...filteredData] + ..sort((a, b) => int.parse(b['id']) - int.parse(a['id'])); + + if (sortedData.isEmpty) return []; + + // Ensure currentPage is valid + final maxPage = (sortedData.length / itemsPerPage).ceil(); + final safePage = currentPage.clamp(1, maxPage); + + final startIndex = (safePage - 1) * itemsPerPage; + final endIndex = (startIndex + itemsPerPage).clamp(0, sortedData.length); + + return sortedData.sublist(startIndex, endIndex); + } + + void filterData(String query) { + print("FilterDAta - $query"); + setState(() { + filteredData = getStaffData.where((item) { + // final isActiveStatus = item['is_active'] == "1" ? "active" : "inactive"; + + return (item['agent_name'] ?? '-').toLowerCase().contains( + query.toLowerCase(), + ) || + (item['reg_no'] ?? '-').toLowerCase().contains( + query.toLowerCase(), + ) || + (item['insurer_name'] ?? '-').toLowerCase().contains( + query.toLowerCase(), + ) || + (item['assigned_to_name'] ?? '-').toLowerCase().contains( + query.toLowerCase(), + ) || + (item['insured_name'] ?? '-').toLowerCase().contains( + query.toLowerCase(), + ) || + (item['premium_amount'] ?? '-').toLowerCase().contains( + query.toLowerCase(), + ) || + (item['payment_mode'] ?? '-').toLowerCase().contains( + query.toLowerCase(), + ) || + (_formatDate(item['created_on']) ?? '-').toLowerCase().contains( + query.toLowerCase(), + ) || + (_formatDate(item['updated_on']) ?? '-').toLowerCase().contains( + query.toLowerCase(), + ) || + (item['policy_number'] ?? '-').toLowerCase().contains( + query.toLowerCase(), + ) || + (item['status'] ?? '-').toLowerCase().contains(query.toLowerCase()); + }).toList(); + }); + } + + final TextEditingController _searchStaffController = TextEditingController(); + + String _formatDate(String rawDate) { + try { + final dateTime = DateTime.parse(rawDate); + return DateFormat( + 'dd-MM-yyyy HH:mm', + ).format(dateTime); // 24-hour format + } catch (e) { + return rawDate; // fallback if parsing fails + } + } + + List _buildPopupMenuActions( + BuildContext context, + dynamic data, + id, + regNum, + ) { + return [ + // if (roleId == 'manager' && data['status'] == 'Awaiting Quotation') ...[ + // Material( + // color: Colors.transparent, + // child: InkWell( + // onTap: () { + // Navigator.pop(context); + // showDialog( + // context: context, + // builder: (ctx) => AssignStaffDialog( + // enquiryPrimaryId: id, + // regNum: regNum, + // userId: 1, + // onSubmit: (value) { + // debugPrint("New assignY: $value"); + // refresh(); + // }, + // ), + // ); + // }, + // hoverColor: Color(0xFFE3F1F0), + // splashColor: Color(0xFFE3F1F0), + // borderRadius: BorderRadius.circular(6), + // child: Padding( + // padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4), + // child: Row( + // // mainAxisSize: MainAxisSize.min, + // children: [ + // Image.asset( + // "assets/miscellaneous/image_3.png", + // // height: 45, + // // width: 15, + // ), + // + // SizedBox(width: 10), + // Text('Assign Staff'), + // ], + // ), + // ), + // ), + // ), + // ], + Material( + color: Colors.transparent, + child: InkWell( + onTap: () async { + Navigator.pop(context); + final prefs = await SharedPreferences.getInstance(); + + // ✅ Remove old value (if any) + await prefs.remove('enqStaffDataId'); + + // ✅ Save the new id + await prefs.setString('enqStaffDataId', id.toString()); + + // Read it back if needed + // final dynamic? enqStaffDataId = prefs.getString('enqStaffDataId'); + + // ✅ Update provider too + // ref.read(quotationStaffIdProvider.notifier).state = enqStaffDataId; + ref.read(quotationStaffIdProvider.notifier).state = id; + + context.go(AppRoutes.quotation); + }, + hoverColor: Color(0xFFE3F1F0), + splashColor: Color(0xFFE3F1F0), + borderRadius: BorderRadius.circular(6), + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4), + child: Row( + // mainAxisSize: MainAxisSize.min, + children: [ + Image.asset( + "assets/miscellaneous/image_1.png", + height: 15, + width: 15, + ), + + const SizedBox(width: 10), + ((data['status'] == 'Policy Created') || + (data['status'] == 'Quotation Accepted')) + ? const Text('View Quotation') + : const Text('Create Quotation'), + ], + ), + ), + ), + ), + + if (data['status'] == 'Quotation Accepted' || + data['status'] == 'Policy Created') + Material( + color: Colors.transparent, + child: InkWell( + onTap: () async { + Navigator.pop(context); + + final prefs = await SharedPreferences.getInstance(); + + // ✅ Remove old value (if any) + await prefs.remove('enqStaffDataId'); + + // ✅ Save the new id + await prefs.setString('enqStaffDataId', id.toString()); + + // ✅ Update provider too + + ref.read(quotationStaffIdProvider.notifier).state = id; + context.go(AppRoutes.policy); + }, + hoverColor: Color(0xFFE3F1F0), + splashColor: Color(0xFFE3F1F0), + borderRadius: BorderRadius.circular(6), + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4), + child: Row( + // mainAxisSize: MainAxisSize.min, + children: [ + Image.asset( + "assets/miscellaneous/image_2.png", + height: 15, + width: 15, + ), + + const SizedBox(width: 10), + data['status'] == 'Policy Created' + ? Text('View Policy') + : Text('Create Policy'), + ], + ), + ), + ), + ), + ]; + } + + @override + Widget build(BuildContext context) { + return MainLayout( + title: "Enquiries", + body: Container( + // color: Colors.yellow.shade50, + width: MediaQuery.of(context).size.width, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Container( + height: 40, + 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), + onPressed: () { + context.go(AppRoutes.dashboard); + }, + splashRadius: 28, + hoverColor: Colors.black12, + padding: const EdgeInsets.all(8), + constraints: const BoxConstraints(), + ), + ), + const SizedBox(width: 5), // spacing between icon and text + Text( + "Enquiries", + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.w600, + ), + ), + ], + ), + ), + ), + SizedBox(height: 10), + ResponsiveLayout.isMobile(context) + ? Container( + height: MediaQuery.of(context).size.height * 0.69, + child: SingleChildScrollView( + child: Padding( + padding: EdgeInsets.all(8), + child: _buildContent(context), + ), + ), + ) + : Expanded( + child: Container( + width: MediaQuery.of(context).size.width, + padding: EdgeInsets.all(8.0), + child: _buildContent(context), + ), + ), + + // Expanded( + // child: Container( + // // color: Colors.green, + // // color: Colors.green.shade50, + // width: MediaQuery.of(context).size.width, + // + // padding: EdgeInsets.all(8.0), + // child: + // ), + // ), + Container( + // height: 20, + width: MediaQuery.of(context).size.width, + // color: Colors.green.shade50, + child: PaginationControls( + currentPage: currentPage, + itemsPerPage: itemsPerPage, + // totalItems: dataVal.length, + totalItems: filteredData.length, + // activeColor: layoutColor, // your theme color + onPageChanged: (page) { + setState(() { + currentPage = page; + }); + }, + onItemsPerPageChanged: (items) { + setState(() { + itemsPerPage = items; + currentPage = 1; + }); + }, + ), + ), + ], + ), + ), + ); + } + + Widget _buildContent(BuildContext context) { + return Column( + children: [ + if (ResponsiveLayout.isMobile(context)) ...[ + Container( + padding: EdgeInsets.all(8.0), + color: Color(0xffD9EBE8), + child: DateFilterRow( + startController: controllers['startDate']!, + endController: controllers['endDate']!, + formKey: _formKey, + isMobile: ResponsiveLayout.isMobile(context), + onFilter: () { + // call your filter logic + filterDateRange(); + }, + onRefresh: () { + // call your refresh logic + refrshfilterDateRange(); + }, + ), + ), + ], + + Container( + // height: 40, + // color: Colors.pink, + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + if (!ResponsiveLayout.isMobile(context)) ...[ + DateFilterRow( + startController: controllers['startDate']!, + endController: controllers['endDate']!, + 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(), + ], + + ThemedSearchField( + hintText: 'Search', + backgroundColor: Color(0xFFF6F8F8), + onChanged: filterData, + controller: _searchStaffController, + txtwidth: ResponsiveLayout.isMobile(context) + ? MediaQuery.of(context).size.width * 0.7 + : MediaQuery.of(context).size.width * 0.2, + ), + + ResponsiveLayout.isMobile(context) + ? Spacer() + : SizedBox(width: 10), + + ExportBtn( + sheetName: "Enquiry", + fileName: "Enquiry_list", + data: filteredData, + txt: !ResponsiveLayout.isMobile(context) ? true : false, + headers: [ + "created_on", + "updated_on", + "reg_no", + "agent_name", + "assigned_to_name", + "insurer_name", + "insured_name", + "premium_amount", + "payment_mode", + "policy_number", + "status", + ], + ), + + // SizedBox(width: 10), + // GestureDetector( + // onTap: () { + // // print('Export'); + // }, + // child: Container( + // padding: EdgeInsets.all(8.0), + // decoration: BoxDecoration( + // color: Color(0xFF425B5B), + // borderRadius: BorderRadius.circular(8.0), + // ), + // child: Row( + // mainAxisSize: MainAxisSize.min, + // children: [ + // Icon(Icons.add, color: Colors.white), + // + // if (!ResponsiveLayout.isMobile(context)) ...[ + // SizedBox(width: 10), + // + // GestureDetector( + // onTap: () { + // // context.go(AppRoutes.agent / create); + // context.go('/staff/create'); + // }, + // child: Text( + // 'Create New Staff', + // style: TextStyle( + // color: Colors.white, + // fontWeight: FontWeight.w600, + // fontSize: 14, + // ), + // ), + // ), + // ], + // ], + // ), + // ), + // ), + ], + ), + ), + SizedBox(height: 10), + if (!ResponsiveLayout.isMobile(context)) + Container( + decoration: BoxDecoration( + color: Color(0xFFEDF6F5), + borderRadius: BorderRadius.circular(6), + ), + padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 16), + child: const Row( + children: [ + Expanded( + flex: 3, + child: Text('Created Date', style: _headerStyle), + ), + Expanded( + flex: 3, + child: Text('Updated Date', style: _headerStyle), + ), + Expanded(flex: 2, child: Text('Reg.No', style: _headerStyle)), + Expanded(flex: 2, child: Text('Partner', style: _headerStyle)), + Expanded( + flex: 2, + child: Text('Assigned To', style: _headerStyle), + ), + Expanded(flex: 3, child: Text('Insurer', style: _headerStyle)), + Expanded( + flex: 3, + child: Padding( + padding: EdgeInsets.only(left: 8.0), + child: Text('Insured Name', style: _headerStyle), + ), + ), + Expanded(flex: 2, child: Text('Premium', style: _headerStyle)), + Expanded( + flex: 2, + child: Text('Payment Mode', style: _headerStyle), + ), + Expanded( + flex: 2, + child: Text('Policy Number', style: _headerStyle), + ), + + Expanded(flex: 3, child: Text('Status', style: _headerStyle)), + Expanded(flex: 1, child: Text('Action', style: _headerStyle)), + ], + ), + ), + ResponsiveLayout.isMobile(context) + ? _buildDataTable(context) + : Expanded(child: _buildDataTable(context)), + ], + ); + } + + Widget _buildDataTable(BuildContext context) { + if (filteredData.isEmpty) { + return const SizedBox( + height: 50, + child: Center(child: Text('No available data')), + ); + } + + final sortedData = [..._paginatedData]; + + if (ResponsiveLayout.isMobile(context)) { + // Use Column instead of ListView + return Column( + children: List.generate(sortedData.length, (index) { + final startIndex = (currentPage - 1) * itemsPerPage; + final item = sortedData[index]; + final sno = startIndex + index; + return _buildDataCard(item, sno); + }), + ); + } + + // Desktop: keep ListView.builder + return ListView.builder( + itemCount: sortedData.length + 1, + itemBuilder: (context, index) { + if (index == 0) return _buildHeader(); + final startIndex = (currentPage - 1) * itemsPerPage; + final item = sortedData[index - 1]; + final sno = startIndex + index; + return _buildDataRow(item, sno); + }, + ); + } + + // Widget _buildDataTable(BuildContext context) { + // if (filteredData.isEmpty) { + // return const SizedBox( + // height: 50, + // child: Center(child: Text('No available data')), + // ); + // } + // + // final sortedData = [..._paginatedData]; + // return ListView.builder( + // itemCount: ResponsiveLayout.isMobile(context) + // ? sortedData + // .length // only cards for mobile + // : sortedData.length + 1, // +1 for header in desktop + // itemBuilder: (context, index) { + // if (!ResponsiveLayout.isMobile(context) && index == 0) { + // return _buildHeader(); + // } + // + // final startIndex = (currentPage - 1) * itemsPerPage; + // final item = + // sortedData[index - (ResponsiveLayout.isMobile(context) ? 0 : 1)]; + // final sno = startIndex + index; + // + // return !ResponsiveLayout.isMobile(context) + // ? _buildDataRow(item, sno) + // : _buildDataCard(item, sno); + // }, + // ); + // } + + Widget _buildHeader() { + return SizedBox.shrink(); + } + + Widget _buildDataRow(Map item, sno) { + return Container( + padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 16), + // margin: const EdgeInsets.only(top: 10), + decoration: BoxDecoration( + color: Colors.white, + // color: Color(0xFFE0F7F9), + border: const Border( + bottom: BorderSide(color: Color(0xFFEAEAEA), width: 1), + ), + borderRadius: BorderRadius.circular(8), + ), + child: Row( + children: [ + Expanded( + flex: 3, + child: Text( + _formatDate(item['created_on']) ?? '-', + style: _dataBold, + softWrap: true, + maxLines: 3, + ), + ), + Expanded( + flex: 3, + child: Text( + _formatDate(item['updated_on']) ?? '-', + style: _dataBold, + softWrap: true, + maxLines: 3, + ), + ), + Expanded( + flex: 2, + child: Text(item['reg_no'] ?? '-', style: _dataBold), + ), + Expanded( + flex: 2, + child: Text( + _formatDate(item['agent_name']) ?? '-', + style: _dataBold, + ), + ), + Expanded( + flex: 2, + child: Text(item['assigned_to_name'] ?? '-', style: _dataBold), + ), + Expanded( + flex: 3, + child: Text( + item['insurer_name'] ?? '-', + style: _dataBold, + softWrap: true, + maxLines: 3, + ), + ), + Expanded( + flex: 3, + child: Center( + child: Text( + item['insured_name'] ?? '-', + style: _dataBold, + softWrap: true, + maxLines: 3, + ), + ), + ), + Expanded( + flex: 2, + child: Text( + item['premium_amount'] ?? '-', + style: _dataBold, + softWrap: true, + maxLines: 3, + ), + ), + Expanded( + flex: 2, + child: Text( + item['payment_mode'] ?? '-', + style: _dataBold, + softWrap: true, + maxLines: 3, + ), + ), + Expanded( + flex: 2, + child: Text( + item['policy_number'] ?? '-', + style: _dataBold, + softWrap: true, + maxLines: 3, + ), + ), + + Expanded( + flex: 3, + child: Text(item['status'] ?? '-', style: _dataBold), + ), + + Expanded( + flex: 1, + child: Row( + children: [ + PopupMenuButton( + color: Colors.white, + padding: EdgeInsets.zero, + offset: Offset(0, 30), + icon: Icon( + Icons.more_vert, + color: Color(0xFF475569), + size: 14, + ), + itemBuilder: (context) => [ + CustomPopupMenuEntry( + child: Container( + padding: EdgeInsets.symmetric( + horizontal: 8, + vertical: 8, + ), + child: Column( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: _buildPopupMenuActions( + context, + item, + item['id'], + item['reg_no'], + ), + ), + ), + ), + ], + ), + ], + ), + ), + ], + ), + ); + } + + Widget _buildDataCard(Map item, int sno) { + return Container( + margin: const EdgeInsets.symmetric(vertical: 6, horizontal: 8), + padding: const EdgeInsets.all(12), + decoration: BoxDecoration( + color: const Color(0xFFF6FEFD), + borderRadius: BorderRadius.circular(8.0), + border: Border.all(color: const Color(0xffD9EBE8)), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + /// Top row: Reg.No + Action menu + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text(item['reg_no'] ?? '-', style: _headerStyle), + PopupMenuButton( + color: Colors.white, + padding: EdgeInsets.zero, + offset: Offset(0, 30), + icon: Icon(Icons.more_vert, color: Color(0xFF475569), size: 14), + itemBuilder: (context) => [ + CustomPopupMenuEntry( + child: Container( + padding: EdgeInsets.symmetric(horizontal: 8, vertical: 8), + child: Column( + mainAxisSize: MainAxisSize.min, + children: _buildPopupMenuActions( + context, + item, + item['id'], + item['reg_no'], + ), + ), + ), + ), + ], + ), + ], + ), + const Divider(color: Color(0xffD9EBE8), thickness: 0.8), + + /// Company + Status + Row( + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text("Company", style: _cardheaderStyle), + Text(item['insurer_name'] ?? '-', style: _cardBodyStyle), + ], + ), + ), + const SizedBox(width: 5), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text("Status", style: _cardheaderStyle), + Text(item['status'] ?? '-', style: _cardBodyStyle), + ], + ), + ), + ], + ), + const SizedBox(height: 15), + + /// Agents + Assigned To + Row( + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text("Partner", style: _cardheaderStyle), + Text(item['agent_name'] ?? '-', style: _cardBodyStyle), + ], + ), + ), + const SizedBox(width: 5), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text("Assigned To", style: _cardheaderStyle), + Text( + item['assigned_to_name'] ?? '-', + style: _cardBodyStyle, + ), + ], + ), + ), + ], + ), + const SizedBox(height: 15), + + /// Insured Name + Premium + Row( + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text("Insured Name", style: _cardheaderStyle), + Text(item['insured_name'] ?? '-', style: _cardBodyStyle), + ], + ), + ), + const SizedBox(width: 5), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text("Premium", style: _cardheaderStyle), + Text(item['premium_amount'] ?? '-', style: _cardBodyStyle), + ], + ), + ), + ], + ), + const SizedBox(height: 15), + + /// Payment Mode + Policy Number + Row( + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text("Payment Mode", style: _cardheaderStyle), + Text(item['payment_mode'] ?? '-', style: _cardBodyStyle), + ], + ), + ), + const SizedBox(width: 5), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text("Policy Number", style: _cardheaderStyle), + Text(item['policy_number'] ?? '-', style: _cardBodyStyle), + ], + ), + ), + ], + ), + const SizedBox(height: 15), + + /// Date + Remarks + Row( + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text("Date", style: _cardheaderStyle), + Text( + _formatDate(item['updated_on']) ?? '-', + style: _cardBodyStyle, + ), + ], + ), + ), + const SizedBox(width: 5), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text("Remarks", style: _cardheaderStyle), + Text( + item['remarks'] ?? '-', + style: _cardBodyStyle, + maxLines: 3, + ), + ], + ), + ), + ], + ), + ], + ), + ); + } + + static final _dataBold = TextStyle( + fontSize: 14, + + fontWeight: FontWeight.w400, + color: Color(0xFF000000), + ); + + static final _dataSub = TextStyle( + fontSize: 10, + fontWeight: FontWeight.w300, + color: Color(0xFF585757), + ); + + static const _headerStyle = TextStyle( + color: Colors.black, + fontWeight: FontWeight.bold, + ); + static const _cardheaderStyle = TextStyle( + color: Colors.black, + fontWeight: FontWeight.w600, + fontSize: 12, + ); + + static const _cardBodyStyle = TextStyle( + color: Color(0xFF545454), + fontWeight: FontWeight.w400, + fontSize: 12, + ); +} diff --git a/lib/presentation/screens/staff/assignStaff.dart b/lib/presentation/screens/staff/assignStaff.dart index 21304f2..1fa285d 100644 --- a/lib/presentation/screens/staff/assignStaff.dart +++ b/lib/presentation/screens/staff/assignStaff.dart @@ -2,8 +2,10 @@ import 'dart:convert'; 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:http/http.dart' as http; + import 'package:toastification/toastification.dart'; import '../../../../core/config/env.dart'; @@ -12,11 +14,12 @@ import '../../../../data/services/auth_service.dart'; import '../../../../data/utils/toastNotification.dart'; import '../../../../data/utils/validators.dart'; import '../../layouts/responsive_layout.dart'; +import '../../providers/manager_provider.dart'; import '../../themes/indicators/input_field_decoration.dart'; import '../../themes/indicators/text_field_theme.dart'; // 🔹 Custom Dialog Widget -class AssignStaffDialog extends StatefulWidget { +class AssignStaffDialog extends ConsumerStatefulWidget { final dynamic userId; final dynamic regNum; final dynamic enquiryPrimaryId; @@ -32,10 +35,11 @@ class AssignStaffDialog extends StatefulWidget { }); @override - State createState() => _AddDialogState(); + // State createState() => _AddDialogState(); + ConsumerState createState() => _AddDialogState(); } -class _AddDialogState extends State { +class _AddDialogState extends ConsumerState { late ApiService apiService; String? _token; @@ -44,11 +48,18 @@ class _AddDialogState extends State { late TextEditingController controller; Map controllers = {}; + List> getInsurersData = []; + List> filteredInsurersData = []; + String? selectedInsurer; + final _formKey = GlobalKey(); final _formKeyEndrosment = GlobalKey(); final GlobalKey>> dropDownKey = GlobalKey>>(); + final GlobalKey>> + dropDownKeyInsurer = GlobalKey>>(); + List tabHeader = ['regNum']; List> getStaffDetailsData = []; @@ -62,6 +73,7 @@ class _AddDialogState extends State { final data = { "id": widget.enquiryPrimaryId, "assigned_to": selectedStaff, + "insurer_id": selectedInsurer, "updated_by": widget.userId, }; return data; @@ -80,7 +92,16 @@ class _AddDialogState extends State { // 🔹 Init logic here (API calls, token fetch, etc.) _initializeToken(); - getStaffDetails(); + // getStaffDetails(1); + getInsurers(); + Future.microtask(() { + final managerId = ref.watch(managerIdProvider); + final handlerId = ref.watch(handlerIdProvider); + if (handlerId != null) { + print('hansles'); + getStaffDetails(handlerId); + } + }); } Future _initializeToken() async { @@ -88,14 +109,47 @@ class _AddDialogState extends State { print("APISERTOKEN - $_token"); } - Future getStaffDetails() async { + Future getInsurers() async { + print('Insurers called'); + setState(() { + isLoading = true; + }); + + try { + final response = await apiService.fetchMasterDropDown('Insurers'); + + if (response['status'] == 200) { + print('getInsurers - ${response['data']}'); + setState(() { + getInsurersData = List>.from(response['data']); + print('API Data - $getInsurersData'); + + filteredInsurersData = List.from(getInsurersData); + print('originalData - $filteredInsurersData'); + }); + } else { + getInsurersData = []; + filteredInsurersData = []; + } + } catch (e) { + print('Exception occurred: $e'); + } finally { + setState(() { + isLoading = false; + }); + } + } + + Future getStaffDetails(int id) async { print('getStaffDetails called'); setState(() { isLoading = true; }); try { - final response = await apiService.fetchMasterDropDown('Staffs'); + final response = await apiService.fetchStaffListForEnquiryAssignDropDown( + id, + ); if (response['status'] == 200) { print('getStaffDetails - ${response['data']}'); @@ -301,6 +355,7 @@ class _AddDialogState extends State { children: [ buildRegistrationNumber(context), buildSelectStaffMem(context), + buildInsurer(context), ], ), ); @@ -364,7 +419,7 @@ class _AddDialogState extends State { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text("Select Staff Member", style: _textStyle), + Text("Select Staff Member *", style: _textStyle), SizedBox(height: 10), Container( color: Colors.white, @@ -446,10 +501,101 @@ class _AddDialogState extends State { ); } + Widget buildInsurer(BuildContext context) { + Map? selectedInsurerd = filteredInsurersData.firstWhere( + (item) => item['id'].toString() == selectedInsurer, + orElse: () => {}, + ); + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('Insurer *', style: _textStyle), + SizedBox(height: 10), + Container( + decoration: BoxDecoration( + color: Colors.white, + // borderRadius: BorderRadius.circular(10.0), + ), + width: ResponsiveLayout.isMobile(context) + ? null + : MediaQuery.of(context).size.width * 0.26, + // height: 40, + child: DropdownSearch>( + key: dropDownKeyInsurer, + selectedItem: selectedInsurerd.isNotEmpty ? selectedInsurerd : null, + items: (filter, infiniteScrollProps) { + return filteredInsurersData; + }, + + itemAsString: (val) => val['name'].toString(), // what to show + compareFn: (item, selectedItem) => + item['id'] == selectedItem['id'], // ✅ compare by id + validator: (val) { + if (val == null) { + return "Required"; // ✅ error message + } + return null; + }, + decoratorProps: DropDownDecoratorProps( + decoration: + AppInputDecorations.dropdownDecoration( + label: "Select Insurer", + ).copyWith( + filled: true, + fillColor: Color( + 0xFFEDF6F5, + ), // 👈 makes the dropdown input white + ), + ), + + popupProps: PopupProps.menu( + fit: FlexFit.loose, + constraints: BoxConstraints(maxHeight: 250), + menuProps: MenuProps( + backgroundColor: + Colors.white, // 👈 sets dropdown background to white + ), + showSearchBox: true, + searchFieldProps: TextFieldProps( + decoration: InputDecoration( + filled: true, + fillColor: Colors.white, + hintText: "Search Insurer...", + enabledBorder: OutlineInputBorder( + borderSide: BorderSide( + color: Colors.white, + ), // 👈 Normal border + ), + focusedBorder: OutlineInputBorder( + borderSide: BorderSide( + color: Colors.white, + width: 1.5, + ), // 👈 Focused border + ), + ), + ), + // constraints: BoxConstraints(), + ), + + onChanged: (val) { + if (val != null) { + print("Selected Insurer : ${val['name']}"); + print("Id: ${val['id']}"); + selectedInsurer = val['id']; + // controllers['agentId']?.text = val['agent_code']; + // agentId = agent['id']; + } + }, + ), + ), + ], + ); + } + // ------------------- STyle --------------------------------- static final TextStyle _textStyle = TextStyle( - fontSize: 14, + fontSize: 12, fontWeight: FontWeight.w600, ); diff --git a/lib/presentation/screens/staff/quotations/createQuotationPopUp.dart b/lib/presentation/screens/staff/quotations/createQuotationPopUp.dart index 0887435..eb0e0c7 100644 --- a/lib/presentation/screens/staff/quotations/createQuotationPopUp.dart +++ b/lib/presentation/screens/staff/quotations/createQuotationPopUp.dart @@ -66,7 +66,7 @@ class createQuotatDialogState extends State { final GlobalKey>> dropDownKeyInsurer = GlobalKey>>(); - List tabHeader = ['idv', 'premium_Amount']; + List tabHeader = ['idv', 'premium_Amount', 'insurer']; List> getInsuranceTypeData = []; List> filteredInsuranceData = []; @@ -99,7 +99,7 @@ class createQuotatDialogState extends State { // 🔹 Init logic here (API calls, token fetch, etc.) _initializeToken(); getInsuranceType(); - getInsurers(); + // getInsurers(); updateData(); } @@ -135,8 +135,8 @@ class createQuotatDialogState extends State { .selectedQuotationFrmListdata!['insurance_plan_type_id'] ?.toString(); - selectedInsurer = - widget.selectedQuotationFrmListdata!['insurer_id']?.toString() ?? ''; + // selectedInsurer = + // widget.selectedQuotationFrmListdata!['insurer_id']?.toString() ?? ''; String? apiDocPath = widget.selectedQuotationFrmListdata!["additional_uploaded_file_name"]; @@ -465,96 +465,115 @@ class createQuotatDialogState extends State { } Widget buildInsurer(BuildContext context) { - Map? selectedInsurerd = filteredInsurersData.firstWhere( - (item) => item['id'].toString() == selectedInsurer, - orElse: () => {}, - ); return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text('Insurer *', style: _textStyle), SizedBox(height: 10), - Container( - decoration: BoxDecoration( - color: Colors.white, - // borderRadius: BorderRadius.circular(10.0), - ), - width: ResponsiveLayout.isMobile(context) + ThemedFormField( + controller: controllers['insurer']!, + validator: (value) => Validators.requiredField(value, "insurer"), + backgroundColor: Color(0xFFEDF6F5), + readOnly: true, + txtwidth: ResponsiveLayout.isMobile(context) ? null : MediaQuery.of(context).size.width * 0.26, - // height: 40, - child: DropdownSearch>( - key: dropDownKeyInsurer, - selectedItem: selectedInsurerd.isNotEmpty ? selectedInsurerd : null, - items: (filter, infiniteScrollProps) { - return filteredInsurersData; - }, - - itemAsString: (val) => val['name'].toString(), // what to show - compareFn: (item, selectedItem) => - item['id'] == selectedItem['id'], // ✅ compare by id - validator: (val) { - if (val == null) { - return "Required"; // ✅ error message - } - return null; - }, - decoratorProps: DropDownDecoratorProps( - decoration: - AppInputDecorations.dropdownDecoration( - label: "Select Insurer", - ).copyWith( - filled: true, - fillColor: Color( - 0xFFEDF6F5, - ), // 👈 makes the dropdown input white - ), - ), - - popupProps: PopupProps.menu( - fit: FlexFit.loose, - constraints: BoxConstraints(maxHeight: 250), - menuProps: MenuProps( - backgroundColor: - Colors.white, // 👈 sets dropdown background to white - ), - showSearchBox: true, - searchFieldProps: TextFieldProps( - decoration: InputDecoration( - filled: true, - fillColor: Colors.white, - hintText: "Search Insurer...", - enabledBorder: OutlineInputBorder( - borderSide: BorderSide( - color: Colors.white, - ), // 👈 Normal border - ), - focusedBorder: OutlineInputBorder( - borderSide: BorderSide( - color: Colors.white, - width: 1.5, - ), // 👈 Focused border - ), - ), - ), - // constraints: BoxConstraints(), - ), - - onChanged: (val) { - if (val != null) { - print("Selected Insurer : ${val['name']}"); - print("Id: ${val['id']}"); - selectedInsurer = val['id']; - // controllers['agentId']?.text = val['agent_code']; - // agentId = agent['id']; - } - }, - ), ), ], ); } + // Widget buildInsurer(BuildContext context) { + // Map? selectedInsurerd = filteredInsurersData.firstWhere( + // (item) => item['id'].toString() == selectedInsurer, + // orElse: () => {}, + // ); + // return Column( + // crossAxisAlignment: CrossAxisAlignment.start, + // children: [ + // Text('Insurer *', style: _textStyle), + // SizedBox(height: 10), + // Container( + // decoration: BoxDecoration( + // color: Colors.white, + // // borderRadius: BorderRadius.circular(10.0), + // ), + // width: ResponsiveLayout.isMobile(context) + // ? null + // : MediaQuery.of(context).size.width * 0.26, + // // height: 40, + // child: DropdownSearch>( + // key: dropDownKeyInsurer, + // selectedItem: selectedInsurerd.isNotEmpty ? selectedInsurerd : null, + // items: (filter, infiniteScrollProps) { + // return filteredInsurersData; + // }, + // + // itemAsString: (val) => val['name'].toString(), // what to show + // compareFn: (item, selectedItem) => + // item['id'] == selectedItem['id'], // ✅ compare by id + // validator: (val) { + // if (val == null) { + // return "Required"; // ✅ error message + // } + // return null; + // }, + // decoratorProps: DropDownDecoratorProps( + // decoration: + // AppInputDecorations.dropdownDecoration( + // label: "Select Insurer", + // ).copyWith( + // filled: true, + // fillColor: Color( + // 0xFFEDF6F5, + // ), // 👈 makes the dropdown input white + // ), + // ), + // + // popupProps: PopupProps.menu( + // fit: FlexFit.loose, + // constraints: BoxConstraints(maxHeight: 250), + // menuProps: MenuProps( + // backgroundColor: + // Colors.white, // 👈 sets dropdown background to white + // ), + // showSearchBox: true, + // searchFieldProps: TextFieldProps( + // decoration: InputDecoration( + // filled: true, + // fillColor: Colors.white, + // hintText: "Search Insurer...", + // enabledBorder: OutlineInputBorder( + // borderSide: BorderSide( + // color: Colors.white, + // ), // 👈 Normal border + // ), + // focusedBorder: OutlineInputBorder( + // borderSide: BorderSide( + // color: Colors.white, + // width: 1.5, + // ), // 👈 Focused border + // ), + // ), + // ), + // // constraints: BoxConstraints(), + // ), + // + // onChanged: (val) { + // if (val != null) { + // print("Selected Insurer : ${val['name']}"); + // print("Id: ${val['id']}"); + // selectedInsurer = val['id']; + // // controllers['agentId']?.text = val['agent_code']; + // // agentId = agent['id']; + // } + // }, + // ), + // ), + // ], + // ); + // } + Widget buildPremiumAmnt(BuildContext context) { return Column( crossAxisAlignment: CrossAxisAlignment.start, diff --git a/lib/presentation/widgets/drawer_menu.dart b/lib/presentation/widgets/drawer_menu.dart index 439c8b9..1e627f3 100644 --- a/lib/presentation/widgets/drawer_menu.dart +++ b/lib/presentation/widgets/drawer_menu.dart @@ -127,6 +127,8 @@ class DrawerMenuState extends ConsumerState { _hidePopup(); if (roleId == 'agent') { context.go(AppRoutes.enquiryLst); + } else if (roleId == 'handler') { + context.go(AppRoutes.enquiryHandlerLst); } else { context.go(AppRoutes.enquiryForStaff); }