From c6a212bc651b3b76c59c13c974279ef1eadba7d4 Mon Sep 17 00:00:00 2001 From: venbaittech Date: Wed, 15 Oct 2025 18:03:49 +0530 Subject: [PATCH] STAFF_ENQ_REDESIGN --- lib/core/services/api_service.dart | 56 +- .../providers/manager_provider.dart | 3 +- .../screens/Enquiry/enquiry/enquiry_tab.dart | 196 ++- .../Enquiry/enquiry/quotation_tab.dart | 20 +- .../screens/Enquiry/enquiry/tabs.dart | 34 +- .../screens/Enquiry/enquiryList.dart | 32 +- .../Enquiry/policy_claims_endros/claims.dart | 22 +- .../policy_claims_endros/endrosment.dart | 27 +- .../UserManagement/Staff/staffList.dart | 6 +- .../screens/dashboard/dashboard.dart | 105 +- .../screens/handler/enquiryList.dart | 536 ++++--- .../screens/login/login_screen.dart | 24 +- .../screens/staff/Enquiry/enquiry_list.dart | 336 ++-- .../staff/Enquiry/enquiry_list_old.dart | 9 +- .../staff/Enquiry/tabs/createQuotation.dart | 769 +++++++++ .../screens/staff/Enquiry/tabs/policyTab.dart | 1419 +++++++++++++++++ .../staff/Enquiry/tabs/quotationTab.dart | 723 +++++++++ .../screens/staff/Enquiry/tabs/tab.dart | 214 +++ .../screens/staff/assignStaff.dart | 21 +- .../screens/staff/policy/policy.dart | 115 +- .../screens/staff/policy/policy_list.dart | 118 +- .../quotations/createQuotationPopUp.dart | 193 +-- .../themes/indicators/text_field_theme.dart | 1 + 23 files changed, 4300 insertions(+), 679 deletions(-) create mode 100644 lib/presentation/screens/staff/Enquiry/tabs/createQuotation.dart create mode 100644 lib/presentation/screens/staff/Enquiry/tabs/policyTab.dart create mode 100644 lib/presentation/screens/staff/Enquiry/tabs/quotationTab.dart create mode 100644 lib/presentation/screens/staff/Enquiry/tabs/tab.dart diff --git a/lib/core/services/api_service.dart b/lib/core/services/api_service.dart index 332e374..cad0331 100644 --- a/lib/core/services/api_service.dart +++ b/lib/core/services/api_service.dart @@ -338,7 +338,7 @@ class ApiService { } // ----------------------------------- Dashboard ------------------------------------------------- - Future> fetchDashboard(int id, role) async { + Future> fetchDashboard(int id, role, userId) async { // print(_token); if (_token == null) { await _initializeToken(); @@ -349,11 +349,13 @@ 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'); + url = Uri.parse( + '${Env.apiUrl}dashboard/handlerDashboard?manager_id=$id&handler_id=$userId', + ); } else if (role == 'staff') { - url = Uri.parse('${Env.apiUrl}dashboard/staffDashboard?staff_id=$id'); + url = Uri.parse('${Env.apiUrl}dashboard/staffDashboard?staff_id=$userId'); } else { - url = Uri.parse('${Env.apiUrl}dashboard/agentDashboard?agent_id=$id'); + url = Uri.parse('${Env.apiUrl}dashboard/agentDashboard?agent_id=$userId'); } // final url = Uri.parse( // 'https://venbait.in/nhance/partner/dev/api/agent/agentList?manager_id=${managerId}', @@ -559,9 +561,13 @@ class ApiService { final String query; - if (role == 'manager') { + if (role == 'manager' || role == 'handler') { query = 'manager_id=$id'; - } else if (role == 'staff') { + } + // else if (role == 'handler') { + // query = 'handler_id=$id'; + // } + else if (role == 'staff') { query = 'staff_id=$id'; } else { query = 'agent_id=$id'; @@ -595,9 +601,11 @@ class ApiService { } Future> findEnqQuotePolicyView(id) async { - print('findEnqQuotePolicyView - $id'); + print('findEnqQuotePolicyVie2w - $id'); if (_token == null) { + print('findEnqQuotePolicyVie2w 1'); await _initializeToken(); + print('findEnqQuotePolicyVie2w 2'); } final url = Uri.parse( '${Env.apiUrl}enquiry/enquiryQuotePolicyView?enquiry_id=$id', @@ -606,7 +614,17 @@ class ApiService { 'Authorization': 'Bearer $_token' ?? '', 'App-Signature': 'nhance-partner-2025-signature-35468846JRhH551HK', }; + print('findEnqQuotePolicyVie2w 3'); final response = await _makeGetRequest(url, headers); + + if (response.containsKey('error')) { + print( + '❌ API returned invalid JSON or HTML. Raw response: ${response['raw']}', + ); + return {}; // return empty map on error + } + print('findEnqQuotePolicyView 1'); + print('findEnqQuotePolicyView 1 - $response'); return response; } @@ -844,7 +862,7 @@ class ApiService { return response; } - // --------------------------------- MASTER DATA ---------------------------------------------- + // --------------------------------- MASTER DATA DROPDOWN---------------------------------------------- Future> fetchMasterDropDown(String val) async { // print(_token); @@ -925,4 +943,26 @@ class ApiService { print('fetchHandlerNameDropDown 4 - $response'); return response; } + + Future> fetchAgentNameDropDown(id) async { + print('fetchAGENTNameDropDown'); + if (_token == null) { + await _initializeToken(); + } + + dynamic url; + print('fetchAGENTNameDropDown 1'); + url = Uri.parse( + '${Env.apiUrl}agent/agentListForEnquiryCreationDropdown?manager_id=$id', + ); + print('fetchAGENTNameDropDown 2'); + final headers = { + 'Authorization': 'Bearer $_token' ?? '', + 'App-Signature': 'nhance-partner-2025-signature-35468846JRhH551HK', + }; + print('fetchAGENTNameDropDown 3'); + final response = await _makeGetRequest(url, headers); + print('fetchAGENTNameDropDown 4 - $response'); + return response; + } } diff --git a/lib/presentation/providers/manager_provider.dart b/lib/presentation/providers/manager_provider.dart index 499244d..0508ce3 100644 --- a/lib/presentation/providers/manager_provider.dart +++ b/lib/presentation/providers/manager_provider.dart @@ -1,6 +1,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; final managerIdProvider = StateProvider((ref) => null); -final handlerIdProvider = StateProvider((ref) => null); +// final handlerIdProvider = StateProvider((ref) => null); final userIdProvider = StateProvider((ref) => null); final enquiryIdProvider = StateProvider((ref) => null); +final navFromEnqStaffProvider = StateProvider((ref) => null); diff --git a/lib/presentation/screens/Enquiry/enquiry/enquiry_tab.dart b/lib/presentation/screens/Enquiry/enquiry/enquiry_tab.dart index 4fb1950..9be9ec8 100644 --- a/lib/presentation/screens/Enquiry/enquiry/enquiry_tab.dart +++ b/lib/presentation/screens/Enquiry/enquiry/enquiry_tab.dart @@ -6,6 +6,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:go_router/go_router.dart'; import 'package:http/http.dart' as http; import 'package:nhance_partner/presentation/layouts/responsive_layout.dart'; +import 'package:nhance_partner/presentation/providers/userRoleProvider.dart'; import 'package:toastification/toastification.dart'; import 'package:universal_html/html.dart' as html; @@ -45,6 +46,8 @@ class EnquiryTabState extends ConsumerState { GlobalKey>>(); final GlobalKey>> dropDownKeyInsurer = GlobalKey>>(); + final GlobalKey>> dropDownKeyAgent = + GlobalKey>>(); List tabHeader = [ 'name', @@ -79,26 +82,32 @@ class EnquiryTabState extends ConsumerState { String? selectedVehicleType; // int? selectedVehicleTypeId; String? selectedInsurer; + String? selectedAgent; Map controllers = {}; String? _token; dynamic userId; dynamic managerId; + dynamic role; List> getVehicleTypeData = []; List> filteredVechicleData = []; + List> getAgentListData = []; + List> filteredAgentData = []; + List> getInsurersData = []; List> filteredInsurersData = []; Map dataDetails() { final data = { - "agent_id": userId, + "agent_id": role == 'handler' ? selectedAgent : userId, "name": controllers["name"]?.text, "mobile": controllers["mobile"]?.text, "email": controllers["email"]?.text, "reg_no": controllers["regNo"]?.text, "vehicle_type_id": selectedVehicleType, + "is_data_created_by_handler": role == 'handler' ? '1' : '0', // "insurer_id": selectedInsurer, // "rc_file_name": "rc_doc.pdf", // "id_proof_file_name": "id_proof.pdf", @@ -129,6 +138,12 @@ class EnquiryTabState extends ConsumerState { Future.microtask(() { managerId = ref.watch(managerIdProvider); userId = ref.watch(userIdProvider); + role = ref.watch(userRoleProvider); + + if (managerId != null) { + print('managerId - $managerId'); + getAgentList(managerId); + } }); getVehicleType(); @@ -215,6 +230,38 @@ class EnquiryTabState extends ConsumerState { } } + Future getAgentList(id) async { + print('getAgentListData called'); + setState(() { + isLoading = true; + }); + + try { + final response = await apiService.fetchAgentNameDropDown(id); + print('getAgentListData called response'); + print('get Agent- ${response['data']}'); + if (response['status'] == 'success') { + print('get Agent- ${response['data']}'); + setState(() { + getAgentListData = List>.from(response['data']); + print('API Data - $getAgentListData'); + + filteredAgentData = List.from(getAgentListData); + print('originalAgentData - $filteredAgentData'); + }); + } else { + getAgentListData = []; + filteredAgentData = []; + } + } catch (e) { + print('Exception occurred: $e'); + } finally { + setState(() { + isLoading = false; + }); + } + } + void updateData() async { if (widget.data != null && widget.data != 'tab' && widget.id != null) { // dynamic response = await apiService.findEnqQuotePolicyView(widget.id!); @@ -234,6 +281,7 @@ class EnquiryTabState extends ConsumerState { isActive = data["is_active"]; selectedVehicleType = data['vehicle_type_id'] ?? ''; selectedInsurer = data['insurer_id'] ?? ''; + selectedAgent = data['agent_id'] ?? ''; String? apiDocPath = data["certificate_file_name"]; @@ -325,26 +373,6 @@ class EnquiryTabState extends ConsumerState { final dataSet = dataDetails(); - // check RC file - // if ((docUploadedRCFile == null || - // (docUploadedRCFile?.bytes == null && - // docUploadedRCFile?.path == null)) && - // (rcFileUrlFromApi == null || rcFileUrlFromApi!.isEmpty)) { - // setState(() => isSaving = false); // reset button - // ToastHelper.showErrorToast(context, "Please upload RC Document"); - // return; - // } - // - // // check ID proof - // if ((docUploadedIDProof == null || - // (docUploadedIDProof?.bytes == null && - // docUploadedIDProof?.path == null)) && - // (idProofFileUrlFromApi == null || idProofFileUrlFromApi!.isEmpty)) { - // setState(() => isSaving = false); // reset button - // ToastHelper.showErrorToast(context, "Please upload ID Proof"); - // return; - // } - try { await createUserData(dataSet); // API call // ToastHelper.showInfoToast(context, "Enquiry saved successfully"); @@ -484,13 +512,24 @@ class EnquiryTabState extends ConsumerState { ToastHelper.showSuccessToast(context, 'Saved Enquiry'); print("Response: ${response.body}"); - if (isUpdating) { - context.go(AppRoutes.tabEnquiry); + if (role == 'handler') { + print('Im handler'); + print('ROle - $role'); + if (isUpdating) { + context.go(AppRoutes.enquiryHandlerLst); + } else { + ref.read(enquiryIdProvider.notifier).state = null; + context.go(AppRoutes.enquiryHandlerLst); + } } else { - ref.read(enquiryIdProvider.notifier).state = null; - context.go(AppRoutes.enquiryLst); + print('Im agent'); + if (isUpdating) { + context.go(AppRoutes.tabEnquiry); + } else { + ref.read(enquiryIdProvider.notifier).state = null; + context.go(AppRoutes.enquiryLst); + } } - setState(() => isSaving = false); } else { print("❌ Submission failed. Status: ${response.statusCode}"); @@ -582,16 +621,28 @@ class EnquiryTabState extends ConsumerState { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ + ?(role == 'handler') + ? _buildResponsiveRow( + context, + buildAgentName(context), + SizedBox.shrink(), + ) + : null, + ?(role == 'handler') + ? isMobile + ? SizedBox(height: 5) + : SizedBox(height: 15) + : null, _buildResponsiveRow(context, buildName(context), buildEmail(context)), - isMobile ? SizedBox(height: 5) : SizedBox(height: 20), + isMobile ? SizedBox(height: 5) : SizedBox(height: 15), _buildResponsiveRow( context, buildPhNumber(context), buildId(context), ), - isMobile ? SizedBox(height: 5) : SizedBox(height: 20), + isMobile ? SizedBox(height: 5) : SizedBox(height: 15), _buildResponsiveRow( context, @@ -599,7 +650,7 @@ class EnquiryTabState extends ConsumerState { // buildInsurer(context), buildUploadRCDocument(context), ), - isMobile ? SizedBox(height: 5) : SizedBox(height: 20), + isMobile ? SizedBox(height: 5) : SizedBox(height: 15), _buildResponsiveRow( context, @@ -608,7 +659,7 @@ class EnquiryTabState extends ConsumerState { buildUploadPolicyDocument(context), ), - isMobile ? SizedBox(height: 5) : SizedBox(height: 20), + isMobile ? SizedBox(height: 5) : SizedBox(height: 15), _buildResponsiveRow( context, // buildUploadPolicyDocument(context), @@ -927,6 +978,91 @@ class EnquiryTabState extends ConsumerState { ); } + Widget buildAgentName(BuildContext context) { + Map? selectedAgntName = filteredAgentData.firstWhere( + (item) => item['id'].toString() == selectedAgent, + orElse: () => {}, + ); + return buildResponsiveField( + label: "Select Partner *", + field: 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: dropDownKeyAgent, + selectedItem: selectedAgntName.isNotEmpty ? selectedAgntName : null, + items: (filter, infiniteScrollProps) { + return filteredAgentData; + }, + + itemAsString: (val) => val['name'].toString(), // what to show + compareFn: (item, selectedItem) => + item['id'] == selectedItem['id'], // ✅ compare by id + validator: (val) { + if (val == null) { + return "Required"; // ✅ error message + } + return null; + }, + decoratorProps: DropDownDecoratorProps( + decoration: + AppInputDecorations.dropdownDecoration( + label: "Select Partner", + ).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 Partner...", + 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 Partner : ${val['name']}"); + print("Id: ${val['id']}"); + selectedAgent = val['id']; + // controllers['agentId']?.text = val['agent_code']; + // agentId = agent['id']; + } + }, + ), + ), + ); + } + // Widget buildUploadRCDocument(BuildContext context) { // return buildResponsiveUploadField( // label: "Upload RC Document", diff --git a/lib/presentation/screens/Enquiry/enquiry/quotation_tab.dart b/lib/presentation/screens/Enquiry/enquiry/quotation_tab.dart index b0b22ed..3d87d70 100644 --- a/lib/presentation/screens/Enquiry/enquiry/quotation_tab.dart +++ b/lib/presentation/screens/Enquiry/enquiry/quotation_tab.dart @@ -6,6 +6,7 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:http/http.dart' as http; +import 'package:nhance_partner/presentation/providers/userRoleProvider.dart'; import 'package:nhance_partner/presentation/screens/Enquiry/enquiry/tabs.dart'; import '../../../../core/config/env.dart'; import '../../../../core/services/api_service.dart'; @@ -78,6 +79,7 @@ class QuotationTabState extends ConsumerState { String? _token; dynamic userId; dynamic managerId; + dynamic role; List> getVehicleTypeData = []; List> filteredVechicleData = []; @@ -116,6 +118,7 @@ class QuotationTabState extends ConsumerState { Future.microtask(() { managerId = ref.watch(managerIdProvider); userId = ref.watch(userIdProvider); + role = ref.watch(userRoleProvider); getQuotationList(); }); @@ -159,10 +162,11 @@ class QuotationTabState extends ConsumerState { filteredData = List.from(originalData); print('quoationListData - $getQuotationData'); - blockKey = getQuotationData.any((item) => item['status'] == 'Accepted'); + blockKey = getQuotationData.any( + (item) => item['status'] == 'Accepted', + ); print('blockKey- $blockKey'); - // blockKey = getQuotationData.any( // (item) => (item['status']?.toString().toLowerCase() ?? '') == 'approved', // ); @@ -275,7 +279,12 @@ class QuotationTabState extends ConsumerState { throw Exception('Token not found. Please log in.'); } - final Map data = {"id": selectedId, "status": val}; + final Map data = { + "id": selectedId, + "status": val, + "action_by": userId, + "action_user": (role == 'agent') ? 'agent' : 'handler', + }; // data['id'] = selectedId; // Add plan_id for update // data['status'] = val; @@ -307,7 +316,6 @@ class QuotationTabState extends ConsumerState { await widget.onRefresh!(); } - // refresh(); // WidgetsBinding.instance.addPostFrameCallback((_) { // tabKey.currentState?.loadQuotationTab(widget.id ?? ""); @@ -598,7 +606,8 @@ class QuotationTabState extends ConsumerState { _buildResponsiveRow( context, buildPlanType(context), - buildDocuments(context), + SizedBox.shrink(), + // buildDocuments(context), ), // if (!ResponsiveLayout.isMobile(context)) const SizedBox(height: 10), @@ -698,7 +707,6 @@ class QuotationTabState extends ConsumerState { ); } - Widget buildDocuments(BuildContext context) { return buildResponsiveUploadField( label: 'Quotation Documents', diff --git a/lib/presentation/screens/Enquiry/enquiry/tabs.dart b/lib/presentation/screens/Enquiry/enquiry/tabs.dart index 432aafd..13f0ef3 100644 --- a/lib/presentation/screens/Enquiry/enquiry/tabs.dart +++ b/lib/presentation/screens/Enquiry/enquiry/tabs.dart @@ -224,8 +224,8 @@ class TabEnquiryListState extends ConsumerState { children: [ Padding( padding: const EdgeInsets.symmetric( - horizontal: 6, - vertical: 2, + horizontal: 1, + vertical: 1, ), child: GestureDetector( onTap: () { @@ -243,7 +243,7 @@ class TabEnquiryListState extends ConsumerState { children: [ const Icon( Icons.arrow_left_sharp, - size: 25, + size: 20, color: Color(0xFF425B5B), ), // const SizedBox(width: 8), @@ -417,12 +417,20 @@ class TabEnquiryListState extends ConsumerState { return Column( children: [ Padding( - padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2), + padding: const EdgeInsets.symmetric(horizontal: 2, vertical: 2), child: GestureDetector( onTap: () { // context.go(AppRoutes.dashboard); - ref.read(enquiryIdProvider.notifier).state = null; - context.go(AppRoutes.enquiryLst); + // 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: [ @@ -431,12 +439,20 @@ class TabEnquiryListState extends ConsumerState { child: IconButton( icon: const Icon( Icons.arrow_left_sharp, - size: 35, + size: 25, color: Color(0xFF425B5B), ), onPressed: () { - 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); + } + + // ref.read(enquiryIdProvider.notifier).state = null; + // context.go(AppRoutes.enquiryLst); }, splashRadius: 28, hoverColor: Colors.black12, diff --git a/lib/presentation/screens/Enquiry/enquiryList.dart b/lib/presentation/screens/Enquiry/enquiryList.dart index e96f908..474a1b9 100644 --- a/lib/presentation/screens/Enquiry/enquiryList.dart +++ b/lib/presentation/screens/Enquiry/enquiryList.dart @@ -350,7 +350,7 @@ class EnquiryListState extends ConsumerState { mainAxisAlignment: MainAxisAlignment.start, children: [ Container( - height: 40, + height: 30, width: MediaQuery.of(context).size.width, child: GestureDetector( onTap: () { @@ -395,7 +395,7 @@ class EnquiryListState extends ConsumerState { ), ), - SizedBox(height: ResponsiveLayout.isMobile(context) ? 5 : 10), + SizedBox(height: ResponsiveLayout.isMobile(context) ? 5 : 5), ResponsiveLayout.isMobile(context) ? Container( height: MediaQuery.of(context).size.height * 0.7, @@ -593,14 +593,18 @@ class EnquiryListState extends ConsumerState { Expanded(flex: 1, child: Text('S.No.', style: _headerStyle)), Expanded( flex: 2, - child: Text('Created Date', style: _headerStyle), + child: Text('Received Date & Time', style: _headerStyle), ), + + Expanded( + flex: 2, + child: Text('Vehicle.No.', style: _headerStyle), + ), + Expanded(flex: 3, child: Text('Insurer', style: _headerStyle)), Expanded( flex: 2, child: Text('Updated Date', style: _headerStyle), ), - Expanded(flex: 1, child: Text('Reg.No.', style: _headerStyle)), - Expanded(flex: 3, child: Text('Company', style: _headerStyle)), Expanded(flex: 2, child: Text('Status', style: _headerStyle)), Expanded(flex: 2, child: Text('Remarks', style: _headerStyle)), Expanded(flex: 1, child: Text('Action', style: _headerStyle)), @@ -707,15 +711,9 @@ class EnquiryListState extends ConsumerState { style: _dataBold, ), ), + Expanded( flex: 2, - child: Text( - _formatDate(item['updated_on']) ?? '-', - style: _dataBold, - ), - ), - Expanded( - flex: 1, child: Text(item['reg_no'] ?? '-', style: _dataBold), ), Expanded( @@ -727,7 +725,13 @@ class EnquiryListState extends ConsumerState { maxLines: 3, ), ), - + Expanded( + flex: 2, + child: Text( + _formatDate(item['updated_on']) ?? '-', + style: _dataBold, + ), + ), Expanded( flex: 2, child: Text(item['status'] ?? '-', style: _dataBold), @@ -919,7 +923,7 @@ class EnquiryListState extends ConsumerState { mainAxisAlignment: MainAxisAlignment.start, children: [ - Text("Company", style: _cardheaderStyle), + Text("Insurer", style: _cardheaderStyle), Text( item['insurer_name'] ?? '-', style: _cardBodyStyle, diff --git a/lib/presentation/screens/Enquiry/policy_claims_endros/claims.dart b/lib/presentation/screens/Enquiry/policy_claims_endros/claims.dart index 2abb45e..d3e67d1 100644 --- a/lib/presentation/screens/Enquiry/policy_claims_endros/claims.dart +++ b/lib/presentation/screens/Enquiry/policy_claims_endros/claims.dart @@ -178,7 +178,7 @@ class claimListState extends ConsumerState { mainAxisAlignment: MainAxisAlignment.start, children: [ Container( - height: 40, + height: 30, width: MediaQuery.of(context).size.width, child: GestureDetector( onTap: () { @@ -191,8 +191,14 @@ class claimListState 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), @@ -212,7 +218,7 @@ class claimListState extends ConsumerState { ), ), - SizedBox(height: 10), + SizedBox(height: 5), Expanded( child: Container( @@ -301,7 +307,7 @@ class claimListState extends ConsumerState { ], ), ), - SizedBox(height: 10), + SizedBox(height: 5), if (!ResponsiveLayout.isMobile(context)) Container( decoration: BoxDecoration( @@ -320,11 +326,11 @@ class claimListState extends ConsumerState { ), Expanded( flex: 2, - child: Text('Reg.No.', style: _headerStyle), + child: Text('Vehicle.No.', style: _headerStyle), ), Expanded( flex: 2, - child: Text('Company', style: _headerStyle), + child: Text('Insurer', style: _headerStyle), ), Expanded( flex: 2, @@ -579,7 +585,7 @@ class claimListState extends ConsumerState { mainAxisAlignment: MainAxisAlignment.start, children: [ //company - Text("Company", style: _cardheaderStyle), + Text("Insurer", style: _cardheaderStyle), Text( item['policy_end_date'] != null ? _formatDate(item['policy_end_date']) diff --git a/lib/presentation/screens/Enquiry/policy_claims_endros/endrosment.dart b/lib/presentation/screens/Enquiry/policy_claims_endros/endrosment.dart index 3de8e53..026cfa0 100644 --- a/lib/presentation/screens/Enquiry/policy_claims_endros/endrosment.dart +++ b/lib/presentation/screens/Enquiry/policy_claims_endros/endrosment.dart @@ -43,8 +43,9 @@ class endosementState extends ConsumerState { final roleId = ref.read(userRoleProvider); final userId = ref.read(userIdProvider); print("F46 => r : $roleId | mId: $id | uId: $userId "); - if (userId != null) { getStaffList(userId, roleId); } - + if (userId != null) { + getStaffList(userId, roleId); + } }); } @@ -174,7 +175,7 @@ class endosementState extends ConsumerState { mainAxisAlignment: MainAxisAlignment.start, children: [ Container( - height: 40, + height: 30, width: MediaQuery.of(context).size.width, child: GestureDetector( onTap: () { @@ -187,8 +188,14 @@ class endosementState 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), @@ -208,7 +215,7 @@ class endosementState extends ConsumerState { ), ), - SizedBox(height: 10), + SizedBox(height: 5), Expanded( child: Container( @@ -295,7 +302,7 @@ class endosementState extends ConsumerState { ], ), ), - SizedBox(height: 10), + SizedBox(height: 5), if (!ResponsiveLayout.isMobile(context)) Container( decoration: BoxDecoration( @@ -314,11 +321,11 @@ class endosementState extends ConsumerState { ), Expanded( flex: 2, - child: Text('Reg.No.', style: _headerStyle), + child: Text('Vehicle.No.', style: _headerStyle), ), Expanded( flex: 4, - child: Text('Company', style: _headerStyle), + child: Text('Insurer', style: _headerStyle), ), Expanded( flex: 2, @@ -548,7 +555,7 @@ class endosementState extends ConsumerState { mainAxisAlignment: MainAxisAlignment.start, children: [ //company - Text("Company", style: _cardheaderStyle), + Text("Insurer", style: _cardheaderStyle), Text( item['insurer_name'] ?? '-', style: _cardBodyStyle, diff --git a/lib/presentation/screens/UserManagement/Staff/staffList.dart b/lib/presentation/screens/UserManagement/Staff/staffList.dart index b918323..a44a6ce 100644 --- a/lib/presentation/screens/UserManagement/Staff/staffList.dart +++ b/lib/presentation/screens/UserManagement/Staff/staffList.dart @@ -43,9 +43,9 @@ class StaffListState extends ConsumerState { Future.microtask(() { final data1 = ref.read(managerIdProvider); - final data2 = ref.read(handlerIdProvider); - print("Edata1 => mId: $data1 -2 : $data2"); - prefid = data2 ?? data1; + // final data2 = ref.read(handlerIdProvider); + print("Edata1 => mId: $data1 -2 :"); + prefid = data1; role = ref.read(userRoleProvider); print("E43 => mId: $prefid"); if (prefid != null && role != null) { diff --git a/lib/presentation/screens/dashboard/dashboard.dart b/lib/presentation/screens/dashboard/dashboard.dart index e6d7cd5..d1d6421 100644 --- a/lib/presentation/screens/dashboard/dashboard.dart +++ b/lib/presentation/screens/dashboard/dashboard.dart @@ -11,13 +11,14 @@ import '../../layouts/main_layout.dart'; import '../../providers/manager_provider.dart'; import '../../providers/quotation_staff_proivder.dart'; import '../../providers/userRoleProvider.dart'; +import '../staff/Enquiry/tabs/tab.dart'; import '../staff/assignStaff.dart'; final enquiriesProvider = Provider>>((ref) { return List.generate(5, (i) { return { "reg": "rrTN64V4387", - "company": "rrNew India", + "Insurer": "rrNew India", "date": "rr09/09/2025", "time": "rr11:30 AM", }; @@ -86,24 +87,25 @@ class _DashboardScreenState extends ConsumerState { apiService = ApiService(); Future.microtask(() { - // final id = ref.read(managerIdProvider); - final prefid = ref.read(userIdProvider); + final prefmanagerid = ref.read(managerIdProvider); + final prefuserid = ref.read(userIdProvider); final prefroleId = ref.read(userRoleProvider); - print("B81 => roleId: $prefroleId | userId: $prefid "); - if (prefid != null) { - getStaffList(prefid, prefroleId); + print("B81 => roleId: $prefroleId | userId: $prefuserid "); + if (prefuserid != null) { + getStaffList(prefmanagerid!, prefroleId, prefuserid); } }); } void refresh() { - final prefid = ref.read(userIdProvider); + final prefmanagerid = ref.read(managerIdProvider); + final prefuserid = ref.read(userIdProvider); final prefroleId = ref.read(userRoleProvider); - if (prefid != null) { - print("REFRESH- calling getStaffList with $prefid, $prefroleId"); - getStaffList(prefid, prefroleId); + if (prefuserid != null) { + print("REFRESH- calling getStaffList with $prefmanagerid, $prefuserid"); + getStaffList(prefmanagerid!, prefroleId, prefuserid); } } @@ -128,31 +130,56 @@ class _DashboardScreenState extends ConsumerState { } Future handleStaffEdit(String id, String? flag) async { - // Navigator.pop(context); - print('EDITStaff'); + // Navigator.pop(context); ddd + dynamic val; print('EDITStaff - $id'); - final prefs = await SharedPreferences.getInstance(); - await prefs.remove('enqStaffDataId'); - // ✅ Save the new id - await prefs.setString('enqStaffDataId', id.toString()); - ref.read(quotationStaffIdProvider.notifier).state = id; - if (flag == "Policies") { - context.go(AppRoutes.policy); + val = 'Policy'; } else { - context.go(AppRoutes.quotation); + val = ''; } + + var enqId = id; + + final prefs = await SharedPreferences.getInstance(); + + // ✅ Remove old value (if any) + await prefs.remove('enqStaffDataId'); + + // ✅ Save the new id + await prefs.setString('enqStaffDataId', enqId.toString()); + await prefs.setString('navFromDashboard', 'Dashboard'); + + ref.read(quotationStaffIdProvider.notifier).state = enqId; + ref.read(navFromEnqStaffProvider.notifier).state = 'Dashboard'; + + // showDialog( + // context: context, + // builder: (context) => TabEnquiryStaffList(showKey: val), + // ); + + final result = await showDialog( + context: context, + barrierDismissible: + false, // optional - prevents closing by tapping outside + builder: (context) => TabEnquiryStaffList(showKey: val), + ); + + // ✅ Code here runs *after* the dialog is closed + print("Dialog closed"); + print("Dialog result: $result"); + refresh(); } - Future getStaffList(int managerId, role) async { + Future getStaffList(int managerId, role, userId) async { print('B89 => Fns called => $managerId | $role'); setState(() { isLoading = true; }); try { - final response = await apiService.fetchDashboard(managerId, role); + final response = await apiService.fetchDashboard(managerId, role, userId); if (response['status'] == 'success') { final data = response['data']; @@ -396,7 +423,7 @@ class _DashboardScreenState extends ConsumerState { children: [ Padding( padding: const EdgeInsets.all(8.0), - child: buildTab("Awaiting Quotation", 0), + child: buildTab("Awaiting Proposal", 0), ), const SizedBox(width: 10), Padding( @@ -457,6 +484,12 @@ class _DashboardScreenState extends ConsumerState { ), color: Colors.white, child: ListTile( + onTap: () { + dynamic id = item['id'].toString(); + + print("Tapped ID: ${item['id']}"); + handleEdit(id); + }, title: Text( item["reg_no"] ?? "-", // from API style: GoogleFonts.inter( @@ -546,6 +579,8 @@ class _DashboardScreenState extends ConsumerState { 0.69 //400 : (role == 'manager') ? MediaQuery.of(context).size.height * 0.5 + : (role == 'staff') + ? MediaQuery.of(context).size.height * 0.85 : MediaQuery.of(context).size.height * 0.5, // 350 adjust height as needed child: (role == 'manager' || role == 'handler') @@ -556,7 +591,7 @@ class _DashboardScreenState extends ConsumerState { title: "Staff Proposal Pending List (${staffQuotationsPendingList.length})", data: staffQuotationsPendingList, - stringFlag: "Quotation", + stringFlag: "Proposal", role: role!, ), ), @@ -568,7 +603,7 @@ class _DashboardScreenState extends ConsumerState { child: (role == 'agent') ? agentPendings( title: - "Awaiting Quotation (${quotationsPendingList.length})", + "Awaiting Proposal (${quotationsPendingList.length})", data: quotationsPendingList, role: role!, onEdit: (row) { @@ -584,7 +619,7 @@ class _DashboardScreenState extends ConsumerState { ? SizedBox.shrink() : agentPendings( title: - "Awaiting Quotation (${quotationsPendingList.length})", + "Awaiting Proposal (${quotationsPendingList.length})", data: quotationsPendingList, role: role!, onEdit: (row) { @@ -619,9 +654,9 @@ class _DashboardScreenState extends ConsumerState { ? SizedBox.shrink() // othersPendings( // title: - // "Awaiting Quotation (${quotationsPendingList.length})", + // "Awaiting Proposal (${quotationsPendingList.length})", // data: quotationsPendingList, - // stringFlag: "Quotation", + // stringFlag: "Proposal", // ) : agentPendings( title: @@ -660,9 +695,9 @@ class _DashboardScreenState extends ConsumerState { ? // ? othersPendings( // title: - // "Awaiting Quotation (${quotationsPendingList.length})", + // "Awaiting Proposal (${quotationsPendingList.length})", // data: quotationsPendingList, - // stringFlag: "Quotation", + // stringFlag: "Proposal", // ) agentPendings( title: @@ -972,7 +1007,7 @@ class agentPendings extends StatelessWidget { // ), // ), Expanded( - flex: 1, + flex: 2, child: Text( "Date & Time", textAlign: TextAlign.center, @@ -982,7 +1017,7 @@ class agentPendings extends StatelessWidget { Expanded( flex: 2, child: Text( - "Registration number", + "Vehicle Number", textAlign: TextAlign.center, style: _headerStyle, ), @@ -991,7 +1026,7 @@ class agentPendings extends StatelessWidget { Expanded( flex: 2, child: Text( - "Insurer Company", + "Insurer", textAlign: TextAlign.center, style: _headerStyle, ), @@ -1072,7 +1107,7 @@ class agentPendings extends StatelessWidget { // ), // Date & Time Expanded( - flex: 1, + flex: 2, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -1691,7 +1726,7 @@ class UnassignedEnq extends StatelessWidget { // ), Expanded( child: Text( - "Registration number", + "Vehicle Number", textAlign: TextAlign.center, style: _headerStyle, ), diff --git a/lib/presentation/screens/handler/enquiryList.dart b/lib/presentation/screens/handler/enquiryList.dart index 313133e..2479b7a 100644 --- a/lib/presentation/screens/handler/enquiryList.dart +++ b/lib/presentation/screens/handler/enquiryList.dart @@ -38,6 +38,8 @@ class EnquiryHandlerState extends ConsumerState { int itemsPerPage = 10; late ApiService apiService; dynamic userId; + dynamic managerId; + dynamic handlerId; final _formKey = GlobalKey(); // List> dataVal = []; @@ -64,10 +66,13 @@ class EnquiryHandlerState extends ConsumerState { final id = ref.read(managerIdProvider); roleId = ref.read(userRoleProvider); userId = ref.read(userIdProvider); + managerId = ref.read(managerIdProvider); + // handlerId = ref.read(handlerIdProvider); - print("C72 => r : $roleId | mId: $id | uId: $userId "); - if (userId != null) { - getStaffList(userId, roleId); + print('handlerIdENQ - $handlerId'); + print("C72 => r : $roleId | mId: $id | uId: $userId !mID : $managerId "); + if (managerId != null) { + getStaffList(managerId, roleId); } }); } @@ -78,8 +83,8 @@ class EnquiryHandlerState extends ConsumerState { final userId = ref.read(userIdProvider); print("C82 => r : $roleId | mId: $id | uId: $userId "); - if (userId != null) { - getStaffList(userId, roleId); + if (managerId != null) { + getStaffList(managerId, roleId); } } @@ -128,47 +133,39 @@ class EnquiryHandlerState extends ConsumerState { String fromDate = '', String toDate = '', }) async { - print('C89 => Fns called => $managerId | $role'); + print('A72 => Fns called => $managerId | $role'); setState(() { isLoading = true; }); try { - final response = await apiService.fetchPolicyList( + final response = await apiService.fetchEnquiryList( 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 = []; @@ -183,17 +180,66 @@ class EnquiryHandlerState extends ConsumerState { } } - 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); - } + // Future getStaffList1( + // int managerId, + // role, { + // String fromDate = '', + // String toDate = '', + // }) async { + // print('C89 => Fns called => $managerId | $role'); + // setState(() { + // isLoading = true; + // }); + // + // try { + // final response = await apiService.fetchPolicyList( + // managerId, + // role, + // fromDate: controllers['startDate']?.text ?? '', + // toDate: controllers['endDate']?.text ?? '', + // ); + // + // print('FromDate : $fromDate'); + // print('ToDate : $toDate'); + // + // if (response['status'] == 'success') { + // final data = response['data']; + // print('C99 => getStaffListData => ${response['data']}'); + // final fromDate = response['from_date'] ?? ''; + // final toDate = response['to_date'] ?? ''; + // + // print('FromDate : $fromDate'); + // print('ToDate : $toDate'); + // setState(() { + // controllers['startDate']?.text = fromDate; + // controllers['endDate']?.text = toDate; + // + // if (data is List) { + // // Already a list of maps + // getStaffData = List>.from(data); + // } else if (data is Map) { + // // Single object, wrap in a list + // getStaffData = [Map.from(data)]; + // } else { + // getStaffData = []; + // } + // // getStaffData = List>.from(response['data']); + // originalData = getStaffData; + // filteredData = List.from(originalData); + // // print('originalData - $getClaimPolicies'); + // }); + // } else { + // getStaffData = []; + // originalData = []; + // } + // } catch (e) { + // print('Exception occurred: $e'); + // } finally { + // setState(() { + // isLoading = false; + // }); + // } + // } List get _paginatedData { // Sort descending by id first @@ -266,149 +312,185 @@ class EnquiryHandlerState extends ConsumerState { } } - List _buildPopupMenuActions( + Future handleStaff( 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'), - ], - ), - ), - ), + ) async { + showDialog( + context: context, + builder: (ctx) => AssignStaffDialog( + enquiryPrimaryId: id, + regNum: regNum, + userId: userId, + onSubmit: (value) { + debugPrint("New assignY: $value"); + refresh(); + }, ), - - 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'), - ], - ), - ), - ), - ), - ]; + ); } + Future handleEdit(item) async { + // Navigator.pop(context); + print('EDITStaff - ${item['id']}'); + + final prefs = await SharedPreferences.getInstance(); + + await prefs.remove('enqAgentDataId'); + + final id = item['id'].toString(); + // ✅ Save the new id + await prefs.setString('enqAgentDataId', id.toString()); + ref.read(enquiryIdProvider.notifier).state = id; + + context.go(AppRoutes.tabEnquiry); + } + + // List _buildPopupMenuActions( + // BuildContext context, + // dynamic data, + // id, + // regNum, + // ) { + // return [ + // if (roleId == 'manager' && data['status'] == 'Awaiting Quotation') ...[ + // Material( + // color: Colors.transparent, + // child: InkWell( + // onTap: () { + // Navigator.pop(context); + // showDialog( + // context: context, + // builder: (ctx) => AssignStaffDialog( + // enquiryPrimaryId: id, + // regNum: regNum, + // userId: 1, + // onSubmit: (value) { + // debugPrint("New assignY: $value"); + // refresh(); + // }, + // ), + // ); + // }, + // hoverColor: Color(0xFFE3F1F0), + // splashColor: Color(0xFFE3F1F0), + // borderRadius: BorderRadius.circular(6), + // child: Padding( + // padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4), + // child: Row( + // // mainAxisSize: MainAxisSize.min, + // children: [ + // Image.asset( + // "assets/miscellaneous/image_3.png", + // // height: 45, + // // width: 15, + // ), + // + // SizedBox(width: 10), + // Text('Assign Staff'), + // ], + // ), + // ), + // ), + // ), + // ], + // + // Material( + // color: Colors.transparent, + // child: InkWell( + // onTap: () async { + // Navigator.pop(context); + // final prefs = await SharedPreferences.getInstance(); + // + // // ✅ Remove old value (if any) + // await prefs.remove('enqStaffDataId'); + // + // // ✅ Save the new id + // await prefs.setString('enqStaffDataId', id.toString()); + // + // // Read it back if needed + // // final dynamic? enqStaffDataId = prefs.getString('enqStaffDataId'); + // + // // ✅ Update provider too + // // ref.read(quotationStaffIdProvider.notifier).state = enqStaffDataId; + // ref.read(quotationStaffIdProvider.notifier).state = id; + // + // context.go(AppRoutes.quotation); + // }, + // hoverColor: Color(0xFFE3F1F0), + // splashColor: Color(0xFFE3F1F0), + // borderRadius: BorderRadius.circular(6), + // child: Padding( + // padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4), + // child: Row( + // // mainAxisSize: MainAxisSize.min, + // children: [ + // Image.asset( + // "assets/miscellaneous/image_1.png", + // height: 15, + // width: 15, + // ), + // + // const SizedBox(width: 10), + // ((data['status'] == 'Policy Created') || + // (data['status'] == 'Quotation Accepted')) + // ? const Text('View Quotation') + // : const Text('Create Quotation'), + // ], + // ), + // ), + // ), + // ), + // + // if (data['status'] == 'Quotation Accepted' || + // data['status'] == 'Policy Created') + // Material( + // color: Colors.transparent, + // child: InkWell( + // onTap: () async { + // Navigator.pop(context); + // + // final prefs = await SharedPreferences.getInstance(); + // + // // ✅ Remove old value (if any) + // await prefs.remove('enqStaffDataId'); + // + // // ✅ Save the new id + // await prefs.setString('enqStaffDataId', id.toString()); + // + // // ✅ Update provider too + // + // ref.read(quotationStaffIdProvider.notifier).state = id; + // context.go(AppRoutes.policy); + // }, + // hoverColor: Color(0xFFE3F1F0), + // splashColor: Color(0xFFE3F1F0), + // borderRadius: BorderRadius.circular(6), + // child: Padding( + // padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4), + // child: Row( + // // mainAxisSize: MainAxisSize.min, + // children: [ + // Image.asset( + // "assets/miscellaneous/image_2.png", + // height: 15, + // width: 15, + // ), + // + // const SizedBox(width: 10), + // data['status'] == 'Policy Created' + // ? Text('View Policy') + // : Text('Create Policy'), + // ], + // ), + // ), + // ), + // ), + // ]; + // } + @override Widget build(BuildContext context) { return MainLayout( @@ -421,7 +503,7 @@ class EnquiryHandlerState extends ConsumerState { mainAxisAlignment: MainAxisAlignment.start, children: [ Container( - height: 40, + height: 30, width: MediaQuery.of(context).size.width, child: GestureDetector( onTap: () { @@ -739,14 +821,17 @@ class EnquiryHandlerState extends ConsumerState { child: const Row( children: [ Expanded( - flex: 3, + flex: 2, child: Text('Created Date', style: _headerStyle), ), Expanded( - flex: 3, + flex: 2, child: Text('Updated Date', style: _headerStyle), ), - Expanded(flex: 2, child: Text('Reg.No', style: _headerStyle)), + Expanded( + flex: 2, + child: Text('Vehicle.No', style: _headerStyle), + ), Expanded(flex: 2, child: Text('Partner', style: _headerStyle)), Expanded( flex: 2, @@ -754,7 +839,7 @@ class EnquiryHandlerState extends ConsumerState { ), Expanded(flex: 3, child: Text('Insurer', style: _headerStyle)), Expanded( - flex: 3, + flex: 2, child: Padding( padding: EdgeInsets.only(left: 8.0), child: Text('Insured Name', style: _headerStyle), @@ -770,7 +855,7 @@ class EnquiryHandlerState extends ConsumerState { child: Text('Policy Number', style: _headerStyle), ), - Expanded(flex: 3, child: Text('Status', style: _headerStyle)), + Expanded(flex: 2, child: Text('Status', style: _headerStyle)), Expanded(flex: 1, child: Text('Action', style: _headerStyle)), ], ), @@ -867,7 +952,7 @@ class EnquiryHandlerState extends ConsumerState { child: Row( children: [ Expanded( - flex: 3, + flex: 2, child: Text( _formatDate(item['created_on']) ?? '-', style: _dataBold, @@ -876,7 +961,7 @@ class EnquiryHandlerState extends ConsumerState { ), ), Expanded( - flex: 3, + flex: 2, child: Text( _formatDate(item['updated_on']) ?? '-', style: _dataBold, @@ -890,10 +975,7 @@ class EnquiryHandlerState extends ConsumerState { ), Expanded( flex: 2, - child: Text( - _formatDate(item['agent_name']) ?? '-', - style: _dataBold, - ), + child: Text(item['agent_name'] ?? '-', style: _dataBold), ), Expanded( flex: 2, @@ -909,14 +991,13 @@ class EnquiryHandlerState extends ConsumerState { ), ), Expanded( - flex: 3, - child: Center( - child: Text( - item['insured_name'] ?? '-', - style: _dataBold, - softWrap: true, - maxLines: 3, - ), + flex: 2, + + child: Text( + item['insured_name'] ?? '-', + style: _dataBold, + softWrap: true, + maxLines: 3, ), ), Expanded( @@ -948,7 +1029,7 @@ class EnquiryHandlerState extends ConsumerState { ), Expanded( - flex: 3, + flex: 2, child: Text(item['status'] ?? '-', style: _dataBold), ), @@ -956,36 +1037,37 @@ class EnquiryHandlerState extends ConsumerState { 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'], - ), - ), - ), + if (item['status'] == 'Awaiting Proposal') + Tooltip( + message: "Assign Staff", + child: IconButton( + icon: Icon(Icons.assignment_ind_outlined, size: 15), + onPressed: () { + handleStaff(context, item, item['id'], item['reg_no']); + }, + splashRadius: 5, + hoverColor: Colors.black12, + padding: const EdgeInsets.all(4), + constraints: const BoxConstraints(), ), - ], + ), + + Tooltip( + message: 'Edit', + child: IconButton( + icon: Image.asset( + "assets/miscellaneous/Edit.png", + height: 12, + width: 12, + ), + onPressed: () { + handleEdit(item); + }, + splashRadius: 5, + hoverColor: Colors.black12, + padding: const EdgeInsets.all(4), + constraints: const BoxConstraints(), + ), ), ], ), @@ -1018,20 +1100,20 @@ class EnquiryHandlerState extends ConsumerState { 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'], - ), - ), - ), - ), + // CustomPopupMenuEntry( + // child: Container( + // padding: EdgeInsets.symmetric(horizontal: 8, vertical: 8), + // child: Column( + // mainAxisSize: MainAxisSize.min, + // children: _buildPopupMenuActions( + // context, + // item, + // item['id'], + // item['reg_no'], + // ), + // ), + // ), + // ), ], ), ], diff --git a/lib/presentation/screens/login/login_screen.dart b/lib/presentation/screens/login/login_screen.dart index dbe40fa..715620c 100644 --- a/lib/presentation/screens/login/login_screen.dart +++ b/lib/presentation/screens/login/login_screen.dart @@ -174,18 +174,18 @@ class _LoginScreenState extends ConsumerState { } // 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"); - } + // final dynamic handlerIdRaw = data['handler_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']; diff --git a/lib/presentation/screens/staff/Enquiry/enquiry_list.dart b/lib/presentation/screens/staff/Enquiry/enquiry_list.dart index 483d486..79d90da 100644 --- a/lib/presentation/screens/staff/Enquiry/enquiry_list.dart +++ b/lib/presentation/screens/staff/Enquiry/enquiry_list.dart @@ -7,6 +7,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:go_router/go_router.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:intl/intl.dart'; +import 'package:nhance_partner/presentation/screens/staff/Enquiry/tabs/tab.dart'; import 'package:shared_preferences/shared_preferences.dart'; import '../../../../core/config/env.dart'; @@ -197,6 +198,21 @@ class EnquiryStaffState extends ConsumerState { } } + Future buildStatusActions(BuildContext context, status, id) async { + print("Actionsstatus - $status -$id"); + dynamic val; + if (status == 'Awaiting Proposal' || status == 'Proposal Created') { + val = ''; + } else { + val = 'Policy'; + } + + showDialog( + context: context, + builder: (context) => TabEnquiryStaffList(showKey: val), + ); + } + List get _paginatedData2 { // Sort descending by id first final sortedData = [...filteredData] @@ -422,6 +438,27 @@ class EnquiryStaffState extends ConsumerState { ]; } + List _buildPopupDownload( + BuildContext context, + dynamic data, + id, + regNum, + ) { + bool isMobile = ResponsiveLayout.isMobile(context); + return [ + Row( + children: [ + buildDocRC(context, isMobile, id), + Spacer(), + // SizedBox(width: 3), + buildDocIdProof(context, isMobile, id), + Spacer(), + buildDocPrevPolicy(context, isMobile, id), + ], + ), + ]; + } + @override Widget build(BuildContext context) { return MainLayout( @@ -723,37 +760,6 @@ class EnquiryStaffState extends ConsumerState { ); } - // 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(); } @@ -803,18 +809,54 @@ class EnquiryStaffState extends ConsumerState { 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), + child: Builder( + builder: (buttonContext) => InkWell( + hoverColor: const Color(0xffD9EBE8), + focusColor: const Color(0xffD9EBE8), + splashColor: const Color(0xffD9EBE8), + highlightColor: const Color(0xffD9EBE8), + onTap: () async { + // ✅ Get overlay and button position RELATIVE to InkWell + final RenderBox button = + buttonContext.findRenderObject() as RenderBox; + final RenderBox overlay = + Overlay.of(buttonContext).context.findRenderObject() + as RenderBox; + + final Offset position = button.localToGlobal( + Offset.zero, + ancestor: overlay, + ); + + await showMenu( + context: buttonContext, + position: RelativeRect.fromLTRB( + position.dx, + position.dy + button.size.height, + overlay.size.width - position.dx, + 0, + ), + items: [ + PopupMenuItem( + child: Column( + mainAxisSize: MainAxisSize.min, + children: _buildPopupDownload( + buttonContext, + item, + item['id'], + item['reg_no'], + ), + ), + ), + ], + color: Colors.white, + ); + }, + child: Text(item['reg_no'] ?? '-', style: _dataBold), + ), ), ), + Expanded( flex: 3, @@ -837,79 +879,24 @@ class EnquiryStaffState extends ConsumerState { ), Expanded( flex: 3, - child: Text(item['status'] ?? '-', style: _dataBold), - ), + child: InkWell( + onTap: () async { + var status = item['status']; + var enqId = item['id']; - // 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'], - // ), - // ), - // ), - // ), - // ], - // ), - // ], - // ), - // ), + final prefs = await SharedPreferences.getInstance(); + + // ✅ Remove old value (if any) + await prefs.remove('enqStaffDataId'); + + // ✅ Save the new id + await prefs.setString('enqStaffDataId', enqId.toString()); + ref.read(quotationStaffIdProvider.notifier).state = enqId; + buildStatusActions(context, status, enqId); + }, + child: Text(item['status'] ?? '-', style: _dataBold), + ), + ), ], ), ); @@ -1101,6 +1088,129 @@ class EnquiryStaffState extends ConsumerState { ); } + Widget buildDocRC(BuildContext context, bool isMobile, selectedEnquiryId) { + return GestureDetector( + onTap: () { + // print("Upload $idget.id}{w"); + final selectedId = selectedEnquiryId; + // final path = + // 'api/agent/downloadAgentIncentiveFile?id=$selectedId'; + final path = + 'api/enquiry/downloadEnquiryFile?enquiry_id=$selectedId&file_type=rc'; + apiService.getPdfDownload(path, selectedId); + }, + child: Container( + padding: const EdgeInsets.symmetric(vertical: 4, horizontal: 6), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(5), + color: const Color(0xFF425B5B), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + "RC", + style: GoogleFonts.inter( + fontSize: 12, + fontWeight: FontWeight.w500, + color: Colors.white, + ), + ), + ?isMobile ? null : SizedBox(width: 2), + // isMobile ? const Spacer() : const SizedBox(width: 15), + const Icon( + Icons.file_download_outlined, + size: 16, + color: Colors.white, + ), + ], + ), + ), + ); + } + + Widget buildDocIdProof( + BuildContext context, + bool isMobile, + selectedEnquiryId, + ) { + return GestureDetector( + onTap: () { + print("Upload $selectedEnquiryId"); + final selectedId = selectedEnquiryId; + // final path = + // 'api/agent/downloadAgentIncentiveFile?id=$selectedId'; + final path = + 'api/enquiry/downloadEnquiryFile?enquiry_id=$selectedId&file_type=id_proof'; + apiService.getPdfDownload(path, selectedId); + }, + child: Container( + padding: const EdgeInsets.symmetric(vertical: 4, horizontal: 6), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(5), + color: const Color(0xFF425B5B), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + "ID Proof", + style: GoogleFonts.inter( + fontSize: 12, + fontWeight: FontWeight.w500, + color: Colors.white, + ), + ), + ?isMobile ? null : SizedBox(width: 2), + // const SizedBox(width: 15), + Icon(Icons.file_download_outlined, size: 16, color: Colors.white), + ], + ), + ), + ); + } + + Widget buildDocPrevPolicy( + BuildContext context, + bool isMobile, + selectedEnquiryId, + ) { + return GestureDetector( + onTap: () { + print("QD597 Upload $selectedEnquiryId"); + final selectedId = selectedEnquiryId; + // final path = + // 'api/agent/downloadAgentIncentiveFile?id=$selectedId'; + final path = + 'api/enquiry/downloadEnquiryFile?enquiry_id=$selectedId&file_type=previous_policy'; + apiService.getPdfDownload(path, selectedId); + }, + child: Container( + padding: const EdgeInsets.symmetric(vertical: 4, horizontal: 6), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(5), + color: const Color(0xFF425B5B), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + "Previous Policy", + style: GoogleFonts.inter( + fontSize: 12, + fontWeight: FontWeight.w500, + color: Colors.white, + ), + ), + ?isMobile ? null : SizedBox(width: 2), + // isMobile ? const Spacer() : const SizedBox(width: 15), + Icon(Icons.file_download_outlined, size: 16, color: Colors.white), + ], + ), + ), + ); + } + static final _dataBold = TextStyle( fontSize: 14, diff --git a/lib/presentation/screens/staff/Enquiry/enquiry_list_old.dart b/lib/presentation/screens/staff/Enquiry/enquiry_list_old.dart index e7f9b2b..975222d 100644 --- a/lib/presentation/screens/staff/Enquiry/enquiry_list_old.dart +++ b/lib/presentation/screens/staff/Enquiry/enquiry_list_old.dart @@ -700,13 +700,16 @@ class EnquiryStaffState extends ConsumerState { children: [ Expanded( flex: 3, - child: Text('Created Date', style: _headerStyle), + child: Text('Received Date & Time', 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('Vehicle.No', style: _headerStyle), + ), Expanded(flex: 2, child: Text('Partner', style: _headerStyle)), Expanded( flex: 2, @@ -1005,7 +1008,7 @@ class EnquiryStaffState extends ConsumerState { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - const Text("Company", style: _cardheaderStyle), + const Text("Insurer", style: _cardheaderStyle), Text(item['insurer_name'] ?? '-', style: _cardBodyStyle), ], ), diff --git a/lib/presentation/screens/staff/Enquiry/tabs/createQuotation.dart b/lib/presentation/screens/staff/Enquiry/tabs/createQuotation.dart new file mode 100644 index 0000000..d15ae13 --- /dev/null +++ b/lib/presentation/screens/staff/Enquiry/tabs/createQuotation.dart @@ -0,0 +1,769 @@ +import 'dart:convert'; + +import 'package:dropdown_search/dropdown_search.dart'; +import 'package:file_picker/file_picker.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:go_router/go_router.dart'; +import 'package:google_fonts/google_fonts.dart'; +import 'package:http/http.dart' as http; +import 'package:nhance_partner/data/utils/toastNotification.dart'; + +import 'package:shared_preferences/shared_preferences.dart'; + +import '../../../../../core/config/env.dart'; +import '../../../../../core/routing/routes.dart'; +import '../../../../../core/services/api_service.dart'; +import '../../../../../data/services/auth_service.dart'; +import '../../../../../data/utils/validators.dart'; +import '../../../../layouts/responsive_layout.dart'; +import '../../../../providers/manager_provider.dart'; +import '../../../../providers/quotation_staff_proivder.dart'; +import '../../../../themes/indicators/customizd_file_upload.dart'; +import '../../../../themes/indicators/input_field_decoration.dart'; +import '../../../../themes/indicators/text_field_theme.dart'; + +class CreateQuotationForm extends ConsumerStatefulWidget { + final dynamic userId; + final dynamic managerId; + final dynamic selectedEnquiryId; + final dynamic selectedInsurdId; + final dynamic selectedQuotationFrmListdata; + final dynamic selectedQuotationFrmListId; + final void Function(String value) onSubmit; + + const CreateQuotationForm({ + super.key, + required this.userId, + required this.managerId, + required this.selectedEnquiryId, + required this.selectedInsurdId, + this.selectedQuotationFrmListdata, + this.selectedQuotationFrmListId, + required this.onSubmit, + }); + + @override + ConsumerState createState() => + CreateQuotationFormState(); + // State createState() => _CreateQuotationFormState(); +} + +class CreateQuotationFormState extends ConsumerState { + late ApiService apiService; + String? _token; + + String? selectedFileNames; + + List> getInsurersData = []; + List> filteredInsurersData = []; + String? selectedInsurer; + + bool isLoading = false; + bool _autoValidate = false; + late TextEditingController controller; + Map controllers = {}; + + final _formKey = GlobalKey(); + + String? docUploadedFileUrlFromApi; + String? selectedId; + PlatformFile? docUploadedFile; + + final GlobalKey>> dropDownKey = + GlobalKey>>(); + + final GlobalKey>> + dropDownKeyInsurer = GlobalKey>>(); + + List tabHeader = ['idv', 'premium_Amount', 'insurer']; + + List> getInsuranceTypeData = []; + List> filteredInsuranceData = []; + + String? selectedInsPlanType; + String? selectedEndorsement; + + Map dataDetails() { + final data = { + "enquiry_id": widget.selectedEnquiryId, + "insured_declared_value": controllers["idv"]?.text, + "insurer_id": widget.selectedInsurdId, + // "insurer_id": selectedInsurer, + "premium_amount": controllers["premium_Amount"]?.text, + "insurance_plan_type_id": selectedInsPlanType, + // "additional_uploaded_file_name": "extra_doc.pdf", + // "created_by": widget.userId, + "manager_id": widget.managerId, + }; + return data; + } + + @override + void initState() { + super.initState(); + apiService = ApiService(); + for (String field in tabHeader) { + controllers[field] = TextEditingController(); + } + + // 🔹 Init logic here (API calls, token fetch, etc.) + _initializeToken(); + getInsuranceType(); + // getInsurers(); + updateData(); + } + + Future _initializeToken() async { + _token = await AuthService.getToken(); + print("APISERTOKEN - $_token"); + } + + @override + void dispose() { + // Dispose all TextEditingControllers + for (var controller in controllers.values) { + controller.dispose(); + } + super.dispose(); + } + + void reset() { + _formKey.currentState?.reset(); + // Clear all TextEditingControllers + for (var controller in controllers.values) { + controller.clear(); + } + dropDownKey.currentState?.changeSelectedItem(null); + dropDownKeyInsurer.currentState?.changeSelectedItem(null); + + // Reset dropdowns / selections + selectedInsurer = null; + selectedInsPlanType = null; + selectedEndorsement = null; + + // Reset file selection + selectedFileNames = null; + docUploadedFile = null; + docUploadedFileUrlFromApi = null; + + // Reset selected ID + selectedId = null; + + // Trigger UI update + setState(() {}); + } + + void updateData() { + if (widget.selectedQuotationFrmListId != null && + widget.selectedQuotationFrmListdata!.isNotEmpty) { + print('checkData -- ${widget.selectedQuotationFrmListdata}'); + selectedId = widget.selectedQuotationFrmListId!; + // Pre-fill controllers + controllers["idv"]?.text = + widget.selectedQuotationFrmListdata!['insured_declared_value'] + ?.toString() ?? + ''; + controllers["premium_Amount"]?.text = + widget.selectedQuotationFrmListdata!['premium_amount']?.toString() ?? + ''; + + selectedInsPlanType = widget + .selectedQuotationFrmListdata!['insurance_plan_type_id'] + ?.toString(); + + selectedInsurer = '1'; + + // controllers["insurer"]?.text = 'LIC'; + + // selectedInsurer = + // widget.selectedQuotationFrmListdata!['insurer_id']?.toString() ?? ''; + + String? apiDocPath = + widget.selectedQuotationFrmListdata!["additional_uploaded_file_name"]; + if (apiDocPath != null && apiDocPath.isNotEmpty) { + print('apiDocPath - $apiDocPath'); + selectedFileNames = apiDocPath.split('/').last; + print('selectedFileNames - $selectedFileNames'); + docUploadedFileUrlFromApi = apiDocPath; + print('passportFileUrlFromApi - $docUploadedFileUrlFromApi'); + docUploadedFile = null; + } else { + selectedFileNames = null; + docUploadedFile = null; + docUploadedFileUrlFromApi = null; + } + } + } + + Future getInsurers() async { + print('Insurers called'); + setState(() { + isLoading = true; + }); + + try { + final response = await apiService.fetchMasterDropDown('Insurers'); + + if (response['status'] == 200) { + print('getInsurers - ${response['data']}'); + setState(() { + getInsurersData = List>.from(response['data']); + print('API Data - $getInsurersData'); + + filteredInsurersData = List.from(getInsurersData); + print('originalData - $filteredInsurersData'); + }); + } else { + getInsurersData = []; + filteredInsurersData = []; + } + } catch (e) { + print('Exception occurred: $e'); + } finally { + setState(() { + isLoading = false; + }); + } + } + + Future getInsuranceType() async { + print('getClaimList called'); + setState(() { + isLoading = true; + }); + + try { + final response = await apiService.fetchMasterDropDown('InsuranceType'); + + if (response['status'] == 200) { + print('getInsuranceTypeData - ${response['data']}'); + setState(() { + getInsuranceTypeData = List>.from( + response['data'], + ); + print('API Data - $getInsuranceTypeData'); + + filteredInsuranceData = List.from(getInsuranceTypeData); + print('originalData - $filteredInsuranceData'); + }); + } else { + getInsuranceTypeData = []; + filteredInsuranceData = []; + } + } catch (e) { + print('Exception occurred: $e'); + } finally { + setState(() { + isLoading = false; + }); + } + } + + void handleDone() { + if (!_formKey.currentState!.validate()) return; + + // setState(() { + // _autoValidate = true; // enable autovalidation after first save attempt + // }); + setState(() { + if (_formKey.currentState!.validate()) { + dataDetails(); + final dataSet = dataDetails(); + print("dataSetAgent - $dataSet"); + // print("managerId - $managerId ,userId - $userId "); + createUserData(dataSet); + } else { + // isDi sable = false; + } + }); + } + + Future createUserData(Map userData) async { + // final bool isUpdating = widget.selectedQuotationFrmListId != null; + // final id = widget.selectedQuotationFrmListId!; + + final bool isUpdating = widget.selectedQuotationFrmListId != null; + final id = widget.selectedQuotationFrmListId; // keep nullable + + print('id - $id'); + final uri = Uri.parse( + // isUpdating + // ? 'https://venbait.in/nhance/partner/dev/api/agent/updateAgent' + // : 'https://venbait.in/nhance/partner/dev/api/agent/createAgent', + isUpdating + ? '${Env.apiUrl}quotation/updateQuotation' + : '${Env.apiUrl}quotation/createQuotation', + ); + if (_token == null) { + throw Exception('Token not found. Please log in.'); + } + + // Use MultipartRequest (POST only) + final request = http.MultipartRequest('POST', uri); + request.headers['Authorization'] = 'Bearer $_token'; + request.headers['app-signature'] = + 'nhance-partner-2025-signature-35468846JRhH551HK'; + + // If updating, spoof the method Laravel-style + if (isUpdating) { + print('Updatrinf'); + // request.fields['_method'] = 'PUT'; + request.fields['id'] = id!; + request.fields['updated_by'] = widget.userId!.toString(); + } else { + print('Not Updatrinf'); + request.fields['created_by'] = widget.userId!.toString(); + } + + print("USerDAta - $userData"); + + // userData.forEach((key, value) { + // request.fields[key] = value.toString(); + // print("✅ Encoded travel_details2: ${request.fields[key]}"); + // }); + + userData.forEach((key, value) { + // if (key != 'certificate_file_name') { + request.fields[key] = value.toString(); + print("✅ Encoded $key: ${request.fields[key]}"); + // } + }); + + if (docUploadedFile != null) { + try { + if (docUploadedFile!.bytes != null) { + final multipartFile = http.MultipartFile.fromBytes( + 'additional_uploaded_file_name', + docUploadedFile!.bytes!, + filename: docUploadedFile!.name, + ); + request.files.add(multipartFile); + } else if (docUploadedFile!.path != null) { + final multipartFile = await http.MultipartFile.fromPath( + 'additional_uploaded_file_name', + docUploadedFile!.path!, + filename: docUploadedFile!.name, + ); + request.files.add(multipartFile); + } + print("📎 File attached: ${docUploadedFile!.name}"); + } catch (e) { + print("❌ Failed to attach file: $e"); + } + } + + print(" Sending request with fields: ${request.fields}"); + + try { + final streamedResponse = await request.send(); + final response = await http.Response.fromStream(streamedResponse); + print("Response status: ${response.statusCode}"); + print("Response body: ${response.body}"); + + if (response.statusCode == 200 || response.statusCode == 201) { + // dispose(); + print("✅ Agent submitted successfully!"); + + print("Response: ${response.body}"); + widget.onSubmit('Success'); + reset(); + // Navigator.of(context).pop(); + } else { + final responseBody = jsonDecode(response.body); + dynamic msg = responseBody['data']; + print("❌ Submission failed. Status: ${response.statusCode}"); + print("Body: ${response.body}"); + + ToastHelper.showErrorToast(context, 'Proposal Creation Failed'); + } + } catch (e) { + print("🔥 Error submitting user: $e"); + } + } + + @override + Widget build(BuildContext context) { + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + // Header row + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + widget.selectedQuotationFrmListId != null + ? 'Update Proposal' + : 'Create Proposal', + style: GoogleFonts.inter( + color: const Color(0xFF374141), + fontSize: 14, + fontWeight: FontWeight.w600, + ), + ), + ], + ), + + const SizedBox(height: 8), + + // 🔹 Switch content dynamically + buildFormFields(context), + ], + ); + } + + Widget buildFormFields(BuildContext context) { + return Form( + key: _formKey, + // autovalidateMode: AutovalidateMode.onUserInteraction, + // autovalidateMode: _autoValidate + // ? AutovalidateMode.onUserInteraction + // : AutovalidateMode.disabled, + child: Row( + crossAxisAlignment: CrossAxisAlignment.end, + + children: [ + buildIdv(context), + Spacer(), + // buildInsurer(context), + buildInsurancePlanType(context), + Spacer(), + buildPremiumAmnt(context), + Spacer(), + // buildDocuments(context), + GestureDetector( + onTap: () { + handleDone(); + // widget.onSubmit(controller.text.trim()); + // Navigator.of(context).pop(); + }, + child: Container( + padding: const EdgeInsets.symmetric( + horizontal: 25.0, + vertical: 15, + ), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(8.0), + color: const Color(0xFF425B5B), + ), + child: const Text( + 'Save', + style: TextStyle( + color: Colors.white, + fontSize: 12, + fontWeight: FontWeight.w600, + ), + ), + ), + ), + + // if (!ResponsiveLayout.isMobile(context)) const SizedBox(height: 10), + // _buildResponsiveRow( + // context, + // buildAdditonalDocuments(context), + // buildRemarks(context), + // ), + ], + ), + ); + } + + // ------------------------- Claims Part ----------------------------------- + + Widget buildIdv(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('IDV *', style: _textStyle), + SizedBox(height: 10), + ThemedFormField( + controller: controllers['idv']!, + validator: (value) => Validators.doubleNumber(value, "IDV"), + // validator: (value) => Validators.number(value, "IDV"), + backgroundColor: Color(0xFFEDF6F5), + // readOnly: true, + txtwidth: ResponsiveLayout.isMobile(context) + ? null + : MediaQuery.of(context).size.width * 0.15, + ), + ], + ); + } + + Widget buildInsurer2(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, + children: [ + Text('Premium Amount *', style: _textStyle), + SizedBox(height: 10), + ThemedFormField( + controller: controllers['premium_Amount']!, + backgroundColor: Color(0xFFEDF6F5), + validator: (value) => Validators.doubleNumber(value, "PremiumAmount"), + // validator: (value) => Validators.number(value, "PremiumAmount "), + txtwidth: ResponsiveLayout.isMobile(context) + ? null + : MediaQuery.of(context).size.width * 0.15, + ), + ], + ); + } + + Widget buildInsurancePlanType(context) { + Map? selectedVehicle = filteredInsuranceData.firstWhere( + (item) => item['id'].toString() == selectedInsPlanType, + orElse: () => {}, + ); + + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text("Plan Type *", style: _textStyle), + SizedBox(height: 10), + Container( + color: Colors.white, + width: ResponsiveLayout.isMobile(context) + ? null + : MediaQuery.of(context).size.width * 0.15, + // height: 40, + child: DropdownSearch>( + key: dropDownKey, + selectedItem: selectedVehicle.isNotEmpty ? selectedVehicle : null, + items: (filter, infiniteScrollProps) { + return filteredInsuranceData; + }, + + itemAsString: (val) => val['insurance_plan_type'].toString(), + compareFn: (item, selectedItem) => + item['id'] == selectedItem['id'], // ✅ compare by id + validator: (val) { + if (val == null) { + return "Required"; // ✅ error message + } + return null; + }, + decoratorProps: DropDownDecoratorProps( + decoration: + AppInputDecorations.dropdownDecoration( + label: "Select Plan Type", + ).copyWith( + filled: true, + fillColor: Color( + 0xFFEDF6F5, + ), // 👈 makes the dropdown input white + ), + ), + + popupProps: PopupProps.menu( + fit: FlexFit.loose, + constraints: BoxConstraints(maxHeight: 250), + menuProps: MenuProps( + backgroundColor: + Colors.white, // 👈 sets dropdown background to white + ), + showSearchBox: true, + searchFieldProps: TextFieldProps( + decoration: InputDecoration( + filled: true, + fillColor: Colors.white, + hintText: "Search Plan Type...", + enabledBorder: OutlineInputBorder( + borderSide: BorderSide( + color: Colors.white, + ), // 👈 Normal border + ), + focusedBorder: OutlineInputBorder( + borderSide: BorderSide( + color: Colors.white, + width: 1.5, + ), // 👈 Focused border + ), + ), + ), + // constraints: BoxConstraints(), + ), + + onChanged: (val) { + if (val != null) { + print("Selected ClaimsType : ${val['insurance_plan_type']}"); + print("Id: ${val['id']}"); + selectedInsPlanType = val['id']; + // controllers['agentId']?.text = val['agent_code']; + // agentId = agent['id']; + } + }, + ), + ), + ], + ); + } + + Widget buildDocuments(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('Proposal Documents ', style: _textStyle), + const SizedBox(height: 10), + Column( + mainAxisAlignment: MainAxisAlignment.end, + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + ThemedUploadField( + hintText: selectedFileNames ?? "Upload Document", + txtwidth: ResponsiveLayout.isMobile(context) + ? null + : MediaQuery.of(context).size.width * 0.26, + backgroundColor: Color(0xFFEDF6F5), + onFileSelected: (fileName, file) { + print("Picked file: $fileName (${file.size} bytes)"); + setState(() { + docUploadedFile = file; + }); + }, + ), + const SizedBox(height: 5), + + if (docUploadedFileUrlFromApi != null) + Container( + // color: Colors.white, + child: Row( + children: [ + GestureDetector( + onTap: () => apiService.downloadFile( + apiUrl: + 'api/quotation/downloadAdditionalUploadedFile?id=$selectedId', + apiId: selectedId, + localFile: docUploadedFile, + fileName: selectedFileNames, + ), + child: Container( + padding: const EdgeInsets.all(5), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(5), + color: Color(0xFF425B5B), + // color: Colors.green.shade300, + ), + child: Row( + children: const [ + Text( + "Download", + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.w200, + color: Colors.white, + ), + ), + SizedBox(width: 5), + Icon(Icons.download, size: 13, color: Colors.white), + ], + ), + ), + ), + ], + ), + ), + ], + ), + ], + ); + } + + // ------------------- STyle --------------------------------- + + static final TextStyle _textStyle = TextStyle( + fontSize: 12, + fontWeight: FontWeight.w600, + ); +} diff --git a/lib/presentation/screens/staff/Enquiry/tabs/policyTab.dart b/lib/presentation/screens/staff/Enquiry/tabs/policyTab.dart new file mode 100644 index 0000000..32b4c07 --- /dev/null +++ b/lib/presentation/screens/staff/Enquiry/tabs/policyTab.dart @@ -0,0 +1,1419 @@ +// import 'dart:io' as html; +import 'package:flutter/foundation.dart'; +import 'package:http/http.dart' as http; +import 'package:dropdown_search/dropdown_search.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:go_router/go_router.dart'; +import 'package:google_fonts/google_fonts.dart'; +import 'package:intl/intl.dart'; +import 'package:shared_preferences/shared_preferences.dart'; + +import 'package:toastification/toastification.dart'; +import 'package:universal_html/html.dart' as html; +import 'package:file_picker/file_picker.dart'; + +import '../../../../../core/config/env.dart'; +import '../../../../../core/routing/routes.dart'; +import '../../../../../core/services/api_service.dart'; +import '../../../../../data/services/auth_service.dart'; +import '../../../../../data/utils/toastNotification.dart'; +import '../../../../../data/utils/validators.dart'; +import '../../../../layouts/main_layout.dart'; +import '../../../../layouts/responsive_layout.dart'; +import '../../../../providers/manager_provider.dart'; +import '../../../../providers/quotation_staff_proivder.dart'; +import '../../../../themes/indicators/customizd_file_upload.dart'; +import '../../../../themes/indicators/date_field_theme.dart'; +import '../../../../themes/indicators/text_field_theme.dart'; + +class PolicyStaffTab extends ConsumerStatefulWidget { + const PolicyStaffTab({super.key}); + @override + ConsumerState createState() => PolicyStaffTabState(); +} + +class PolicyStaffTabState extends ConsumerState { + int currentPage = 0; + int itemsPerPage = 10; + + List> dataVal = []; + + final TextEditingController _searchController = TextEditingController(); + + late ApiService apiService; + final _formKey = GlobalKey(); + final GlobalKey>> dropDownKey = + GlobalKey>>(); + + List tabHeader = [ + 'name', + 'email', + 'mobile', + 'code', + 'address', + 'regNo', + + 'insurer', + 'regNum', + 'idv', + 'premium_amount', + 'planType', + 'policyInsName', + 'policyPremiumAmount', + 'policyIdv', + 'policyNumber', + 'policyPaymentMode', + 'issueDate', + 'startDate', + 'endDate', + ]; + late String isActive = "1"; + PlatformFile? docUploadedFile; + PlatformFile? docPDFUploadedFile; + + // PlatformFile? passportFile; + String? selectedFileNames; + String? selectedPDFFileNames; + String? passportFileUrlFromApi; + String? docUploadedFileUrlFromApi; + String? docUploadedPDFFileUrlFromApi; + + String? selectedId; + bool isLoading = false; + bool isquotation = false; + bool hasPolicyData = false; + + Map controllers = {}; + String? _token; + dynamic userId; + dynamic managerId; + dynamic enqQuotation; + dynamic selectedEnquiryId; + dynamic acceptedQuotationId; + dynamic choosedPolcyId; + + List> getVehicleTypeData = []; + List> filteredVechicleData = []; + + List> getInsurersData = []; + List> filteredInsurersData = []; + + late Map enquiryData; + late Map policyData; + Map? quotationAcceptedData; + + Map dataDetails() { + final data = { + "quotation_id": acceptedQuotationId, + "insured_name": controllers["policyInsName"]?.text, + "issued_date": controllers["issueDate"]?.text, + "start_date": controllers["startDate"]?.text, + "end_date": controllers["endDate"]?.text, + "premium_amount": controllers["premium_amount"]?.text, + // "premium_amount": controllers["policyPremiumAmount"]?.text, + "policy_number": controllers["policyNumber"]?.text, + "payment_mode": controllers["policyPaymentMode"]?.text, + "manager_id": managerId, + // "policy_pdf_file_name": "policy_doc.pdf", + // "policy_payment_receipt_file_name": "receipt_doc.pdf", + "created_by": userId, + }; + return data; + } + + List> quotationData = []; + List> originalData = []; + List> filteredData = []; + // bool isLoading = false; + + @override + void initState() { + super.initState(); + apiService = ApiService(); + + for (String field in tabHeader) { + controllers[field] = TextEditingController(); + } + // updateData(); + _initializeToken(); + // Future.microtask(() { + // managerId = ref.watch(managerIdProvider); + // userId = ref.watch(userIdProvider); + // + // getQuotationList(managerId); + // }); + + if (kIsWeb) { + Future.microtask(() => _restoreManagerId(ref)); + } + + Future.microtask(() { + managerId = ref.read(managerIdProvider); // ✅ use read + userId = ref.read(userIdProvider); // ✅ use read + print('QmanagerId - $managerId'); + print('QuserId - $userId'); + enqQuotation = ref.read(quotationStaffIdProvider); // ✅ use read + print('QenqQuotation - $enqQuotation'); + if (enqQuotation != null) { + _loadData(enqQuotation); + } + }); + } + + Future _restoreManagerId(WidgetRef ref) async { + final prefs = await SharedPreferences.getInstance(); + + final String? savedId = prefs.getString( + 'enqStaffDataId', + ); // already a string + if (savedId != null) { + ref.read(quotationStaffIdProvider.notifier).state = savedId; + print("savedenqStaffDataId ID restored: $savedId"); + + _loadData(savedId); + } + } + + @override + void dispose() { + // Dispose all TextEditingControllers + for (var controller in controllers.values) { + controller.dispose(); + } + super.dispose(); + } + + Future _initializeToken() async { + _token = await AuthService.getToken(); + print("APISERTOKEN - $_token"); + } + + Future _loadData(enqQuotation) async { + getQuotationList(enqQuotation); + } + + Future getQuotationList(enqQuotation) async { + print('getClaimList called enqQuotation- $enqQuotation'); + setState(() { + isLoading = true; + }); + + try { + final response = await apiService.findEnqQuotePolicyView(enqQuotation); + + if (response['status'] == 'success') { + print('quoationListData - ${response['data']}'); + setState(() { + enquiryData = Map.from(response['data']['enquiry']); + print('enquiryData - ${enquiryData}'); + + updateEnquiryData(enquiryData); + // quotationData = List>.from(response['data']); + quotationData = List>.from( + response['data']['quotations'], + ); + print('quotationData - ${quotationData}'); + updateQuotationData(quotationData); + + policyData = Map.from(response['data']['policies']); + + print('policyData - ${policyData}'); + + updatePolicyData(policyData); + originalData = quotationData; + filteredData = List.from(originalData); + // print('originalData - $getClaimPolicies'); + }); + } else { + quotationData = []; + originalData = []; + } + } catch (e) { + print('Exception occurred: $e'); + } finally { + setState(() { + isLoading = false; + }); + } + } + + void updateEnquiryData(enquiryData) { + setState(() { + selectedEnquiryId = enquiryData['id']; + print('selectedEnquiryId- $selectedEnquiryId'); + controllers["regNum"]?.text = enquiryData['reg_no']; + controllers["insurer"]?.text = enquiryData['insurer_name']; + controllers["policyInsName"]?.text = enquiryData['name']; + }); + } + + void updateQuotationData(List> quotationData) { + try { + final acceptedItem = quotationData.firstWhere( + (item) => item['status'] == 'Accepted', + ); + + quotationAcceptedData = acceptedItem; + print('AcceptedData - $quotationAcceptedData'); + + if (quotationAcceptedData != null) { + if (!mounted) return; + setState(() { + controllers["insurer"]?.text = quotationAcceptedData?['insurer_name']; + acceptedQuotationId = quotationAcceptedData?['id']?.toString() ?? ''; + controllers["idv"]?.text = + quotationAcceptedData?['insured_declared_value']?.toString() ?? + ''; + controllers["premium_amount"]?.text = + quotationAcceptedData?['premium_amount']?.toString() ?? ''; + controllers["planType"]?.text = + quotationAcceptedData?['insurance_plan_type']?.toString() ?? ''; + }); + } + } catch (e) { + quotationAcceptedData = null; + print('No Accepted quotation found'); + } + } + + void updatePolicyData(Map policyData) { + if (!mounted || policyData.isEmpty) return; + + setState(() { + hasPolicyData = true; + choosedPolcyId = policyData['id']?.toString() ?? ''; + controllers["policyInsName"]?.text = + policyData['insured_name']?.toString() ?? ''; + controllers["idv"]?.text = + policyData['insured_declared_value']?.toString() ?? ''; + controllers["policyPremiumAmount"]?.text = + policyData['premium_amount']?.toString() ?? ''; + controllers["planType"]?.text = + policyData['insurance_plan_type']?.toString() ?? ''; + controllers["policyNumber"]?.text = + policyData['policy_number']?.toString() ?? ''; + controllers["policyPaymentMode"]?.text = + policyData['payment_mode']?.toString() ?? ''; + controllers["issueDate"]?.text = + policyData['issued_date']?.toString() ?? ''; + controllers["startDate"]?.text = + policyData['start_date']?.toString() ?? ''; + controllers["endDate"]?.text = policyData['end_date']?.toString() ?? ''; + + // RC FILE + String? rcPath = policyData["policy_pdf_file_name"]; + if (rcPath != null && rcPath.isNotEmpty) { + selectedPDFFileNames = rcPath.split('/').last; // UI filename + docUploadedPDFFileUrlFromApi = rcPath; // API download URL + docPDFUploadedFile = null; + // docUploadedRCFile = + // rcFileUrlFromApi as PlatformFile?; // user has not re-uploaded yet + } else { + selectedPDFFileNames = null; // no UI file shown + docUploadedPDFFileUrlFromApi = null; + docPDFUploadedFile = null; + } + + // ID PROOF FILE + String? idProofPath = policyData["policy_payment_receipt_file_name"]; + if (idProofPath != null && idProofPath.isNotEmpty) { + selectedFileNames = idProofPath.split('/').last; + docUploadedFileUrlFromApi = idProofPath; // ✅ FIXED → correct variable + docUploadedFile = null; + // docUploadedIDProof = idProofFileUrlFromApi as PlatformFile?; + } else { + selectedFileNames = null; + docUploadedFileUrlFromApi = null; + docUploadedFile = null; + } + }); + } + + void handleSave() { + if (!_formKey.currentState!.validate()) return; + setState(() { + if (_formKey.currentState!.validate()) { + dataDetails(); + final dataSet = dataDetails(); + print("dataSetPolicy - $dataSet"); + + // File validations + if ((docPDFUploadedFile == null || docPDFUploadedFile?.bytes == null) && + (docUploadedPDFFileUrlFromApi == null || + docUploadedPDFFileUrlFromApi!.isEmpty)) { + ToastHelper.showErrorToast(context, "Policy PDF is required"); + return; + } + + // if ((docUploadedFile == null || docUploadedFile?.bytes == null) && + // (docUploadedFileUrlFromApi == null || + // docUploadedFileUrlFromApi!.isEmpty)) { + // ToastHelper.showErrorToast(context, "Payment Receipt is required"); + // return; + // } + + // print("managerId - $managerId ,userId - $userId "); + createUserData(dataSet); + } else { + // isDi sable = false; + } + }); + } + + Future attachFiles(http.MultipartRequest request) async { + Future addFileOrKeepName( + PlatformFile? file, + String? apiFileName, + String fieldName, + ) async { + if (file != null) { + // User uploaded a new file → send as multipart + if (file.bytes != null) { + request.files.add( + http.MultipartFile.fromBytes( + fieldName, + file.bytes!, + filename: file.name, + ), + ); + } else if (file.path != null) { + request.files.add( + await http.MultipartFile.fromPath( + fieldName, + file.path!, + filename: file.name, + ), + ); + } + print("📎 Attached new file → $fieldName"); + } else if (apiFileName != null && apiFileName.isNotEmpty) { + // No new upload → tell backend to keep old file + request.fields[fieldName] = apiFileName; + print("🔗 Kept old file → $fieldName = $apiFileName"); + } else { + // Nothing at all + request.fields[fieldName] = ""; + } + } + + await addFileOrKeepName( + docPDFUploadedFile, + docUploadedPDFFileUrlFromApi, + 'policy_pdf_file_name', + ); + // await addFileOrKeepName( + // docUploadedFile, + // docUploadedFileUrlFromApi, + // 'policy_payment_receipt_file_name', + // ); + } + + Future createUserData(Map userData) async { + final bool isUpdating = choosedPolcyId != null; + final id = choosedPolcyId; + // print('UPDId- $id'); + + // final bool isUpdating = false; + final uri = Uri.parse( + isUpdating + ? '${Env.apiUrl}policy/updatePolicy' + : '${Env.apiUrl}policy/createPolicy', + ); + if (_token == null) { + throw Exception('Token not found. Please log in.'); + } + + // Use MultipartRequest (POST only) + final request = http.MultipartRequest('POST', uri); + request.headers['Authorization'] = 'Bearer $_token'; + request.headers['app-signature'] = + 'nhance-partner-2025-signature-35468846JRhH551HK'; + + // If updating, spoof the method Laravel-style + if (isUpdating) { + request.fields['id'] = id!; + request.fields['updated_by'] = userId!.toString(); + } else { + request.fields['created_by'] = userId!.toString(); + } + + print("USerDAta - $userData"); + + // userData.forEach((key, value) { + // request.fields[key] = value.toString(); + // print("✅ Encoded travel_details2: ${request.fields[key]}"); + // }); + await attachFiles(request); + userData.forEach((key, value) { + if (key != 'policy_pdf_file_name' + // && + // key != 'policy_payment_receipt_file_name' + ) { + request.fields[key] = value.toString(); + print("✅ Encoded $key: ${request.fields[key]}"); + } else { + print('Something Missing..'); + } + }); + + // attach files + + // request.fields['agent_id'] = selectedId.toString(); + + print(" Sending request with fields: ${request.fields}"); + + try { + final streamedResponse = await request.send(); + final response = await http.Response.fromStream(streamedResponse); + print("Response status: ${response.statusCode}"); + print("Response body: ${response.body}"); + + if (response.statusCode == 200 || response.statusCode == 201) { + // dispose(); + print("✅ Agent submitted successfully!"); + ToastHelper.showSuccessToast(context, 'Saved Policy'); + print("Response: ${response.body}"); + + if (isUpdating) { + context.go(AppRoutes.policylist); + } else { + ref.read(enquiryIdProvider.notifier).state = null; + context.go(AppRoutes.policylist); + } + } else { + print("❌ Submission failed. Status: ${response.statusCode}"); + print("Body: ${response.body}"); + + showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + title: Text("Policy Creation Failed"), + content: Text( + "There was a problem in creating policy. Please try again.", + ), + actions: [ + TextButton( + child: Text("OK"), + onPressed: () { + Navigator.of(context).pop(); + }, + ), + ], + ); + }, + ); + } + } catch (e) { + print("🔥 Error submitting user: $e"); + } + } + + @override + Widget build(BuildContext context) { + return Container( + // height: MediaQuery.of(context).size.height, + // width: MediaQuery.of(context).size.width, + // padding: const EdgeInsets.all(20), + // child: SingleChildScrollView( + // scrollDirection: Axis.vertical, + child: Column( + children: [ + Expanded( + child: Container( + // color: Colors.green, + decoration: BoxDecoration( + color: Color(0xffEDF6F5), + borderRadius: BorderRadius.circular(10.0), + ), + width: MediaQuery.of(context).size.width, + // height: MediaQuery.of(context).size.height * 0.8, + padding: EdgeInsets.all(26.0), + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + // SingleChildScrollView(child: buildFormFields(context)), + buildFormFields(context), + + // const SizedBox(height: 20), + + //confirm button + // if (!hasPolicyData) + Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + GestureDetector( + onTap: () { + handleSave(); + }, + child: Container( + padding: EdgeInsets.symmetric( + horizontal: 45.0, + vertical: 8, + ), + + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(8.0), + color: Color(0xFF425B5B), + ), + child: Text( + 'Submit', + style: TextStyle( + color: Colors.white, + fontSize: 12, + fontWeight: FontWeight.w600, + ), + ), + ), + ), + ], + ), + ], + ), + ), + ), + + // const SizedBox(height: 20), + + // Container( + // decoration: BoxDecoration( + // color: const Color(0xFFEDF6F5), + // borderRadius: BorderRadius.circular(10), + // ), + // padding: const EdgeInsets.all(20), + // child: Column( + // crossAxisAlignment: CrossAxisAlignment.start, + // children: [ + // Text( + // "Enquiry Details", + // style: GoogleFonts.inter( + // fontSize: 16, + // fontWeight: FontWeight.w700, + // ), + // ), + // SizedBox(height: 15), + // Row( + // mainAxisAlignment: MainAxisAlignment.center, + // children: [ + // Expanded(flex: 2, child: registernumber(context)), + // const SizedBox(width: 20), + // Expanded(flex: 2, child: insurer(context)), + // const SizedBox(width: 20), + // Expanded(flex: 2, child: buildIdv(context)), + // const SizedBox(width: 20), + // Expanded(flex: 2, child: buildPremiumAmnt(context)), + // const SizedBox(width: 20), + // Expanded(flex: 2, child: buildPlanType(context)), + // const SizedBox(width: 20), + // ], + // ), + // ], + // ), + // ), + // + // const SizedBox(height: 20), + ], + ), + // ), + ); + } + + Widget buildDocRC(BuildContext context, bool isMobile) { + return GestureDetector( + onTap: () { + // print("Upload $idget.id}{w"); + final selectedId = selectedEnquiryId; + // final path = + // 'api/agent/downloadAgentIncentiveFile?id=$selectedId'; + final path = + 'api/enquiry/downloadEnquiryFile?enquiry_id=$selectedId&file_type=rc'; + apiService.getPdfDownload(path, selectedId); + }, + child: Container( + padding: const EdgeInsets.all(8), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(5), + color: const Color(0xFF425B5B), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const Text( + "Download RC", + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.w200, + color: Colors.white, + ), + ), + ?isMobile ? null : SizedBox(width: 15), + // isMobile ? const Spacer() : const SizedBox(width: 15), + const Icon( + Icons.file_download_outlined, + size: 16, + color: Colors.white, + ), + ], + ), + ), + ); + } + + Widget buildDocIdProof(BuildContext context, bool isMobile) { + return GestureDetector( + onTap: () { + print("Upload $selectedEnquiryId"); + final selectedId = selectedEnquiryId; + // final path = + // 'api/agent/downloadAgentIncentiveFile?id=$selectedId'; + final path = + 'api/enquiry/downloadEnquiryFile?enquiry_id=$selectedId&file_type=id_proof'; + apiService.getPdfDownload(path, selectedId); + }, + child: Container( + padding: const EdgeInsets.all(8), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(5), + color: const Color(0xFF425B5B), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + "Download ID Proof", + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.w200, + color: Colors.white, + ), + ), + ?isMobile ? null : SizedBox(width: 15), + // const SizedBox(width: 15), + Icon(Icons.file_download_outlined, size: 16, color: Colors.white), + ], + ), + ), + ); + } + + Widget buildDocPrevPolicy(BuildContext context, bool isMobile) { + return GestureDetector( + onTap: () { + print("Upload $selectedEnquiryId"); + final selectedId = selectedEnquiryId; + // final path = + // 'api/agent/downloadAgentIncentiveFile?id=$selectedId'; + final path = + 'api/enquiry/downloadEnquiryFile?enquiry_id=$selectedId&file_type=previous_policy'; + apiService.getPdfDownload(path, selectedId); + }, + child: Container( + padding: const EdgeInsets.all(8), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(5), + color: const Color(0xFF425B5B), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + "Download Previous Policy", + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.w200, + color: Colors.white, + ), + ), + ?isMobile ? null : SizedBox(width: 15), + // isMobile ? const Spacer() : const SizedBox(width: 15), + Icon(Icons.file_download_outlined, size: 16, color: Colors.white), + ], + ), + ), + ); + } + + Widget buildDocQuotation(BuildContext context, bool isMobile) { + return GestureDetector( + onTap: () { + print("Upload $selectedEnquiryId"); + final selectedId = acceptedQuotationId; + // final path = + // 'api/agent/downloadAgentIncentiveFile?id=$selectedId'; + // final path = + // 'api/enquiry/downloadEnquiryFile?enquiry_id=$selectedId&file_type=previous_policy'; + final path = + 'api/quotation/downloadAdditionalUploadedFile?id=$acceptedQuotationId'; + + apiService.getPdfDownload(path, selectedId); + }, + child: Container( + padding: const EdgeInsets.all(8), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(5), + color: const Color(0xFF425B5B), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + "Quotation Documents", + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.w200, + color: Colors.white, + ), + ), + ?isMobile ? null : SizedBox(width: 15), + // isMobile ? const Spacer() : const SizedBox(width: 15), + Icon(Icons.file_download_outlined, size: 16, color: Colors.white), + ], + ), + ), + ); + } + + Widget buildFormFields(BuildContext context) { + return Form( + key: _formKey, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _buildResponsiveRow( + context, + buildUploadPolicyPdf(context), + Paymentmode(context), + policynumber(context), + ), + const SizedBox(height: 15), + Text("Policy TimePeriod", style: _textStrongStyle), + if (!ResponsiveLayout.isMobile(context)) const SizedBox(height: 15), + _buildResponsiveRow( + context, + + policyissuedate(context), + policystartdate(context), + policyenddate(context), + ), + + // if (!ResponsiveLayout.isMobile(context)) const SizedBox(height: 10), + // + // + // _buildResponsiveRow( + // context, + // Insuredname(context), + // premiumamount(context), + // policynumber(context), + // ), + // if (!ResponsiveLayout.isMobile(context)) const SizedBox(height: 10), + // + // _buildResponsiveRow( + // context, + // buildUploadPolicyReceipt(context), + // ), + + // if (!ResponsiveLayout.isMobile(context)) const SizedBox(height: 10), + // _buildResponsiveRow( + // context, + // buildAdditonalDocuments(context), + // buildRemarks(context), + // ), + ], + ), + ); + } + + Widget _buildResponsiveRow( + BuildContext context, + Widget first, + Widget second, + Widget third, + ) { + if (ResponsiveLayout.isMobile(context)) { + // Stack vertically + return Column(children: [first, const SizedBox(height: 2), second]); + } else { + // Place side by side + return Row( + children: [ + Expanded(child: first), + const SizedBox(width: 25), + Expanded(child: second), + const SizedBox(width: 25), + Expanded(child: third), + ], + ); + } + } + + // Widget buildName(BuildContext context) { + // return buildResponsiveField( + // label: "Registration Number", + // field: ThemedFormField( + // controller: controllers['name']!, + // validator: (value) => Validators.requiredField(value, "name"), + // txtwidth: ResponsiveLayout.isMobile(context) + // ? null + // : MediaQuery.of(context).size.width * 0.26, + // ), + // ); + // } + // + // Widget buildInsusrer(BuildContext context) { + // return buildResponsiveField( + // label: "Insusrer", + // field: ThemedFormField( + // controller: controllers['email']!, + // validator: (value) => Validators.email(value, "email"), + // txtwidth: ResponsiveLayout.isMobile(context) + // ? null + // : MediaQuery.of(context).size.width * 0.26, + // ), + // ); + // } + + Widget registernumber(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('Registration Number*', style: _textStyle), + SizedBox(height: 10), + ThemedFormField( + controller: controllers['regNum']!, + // validator: (value) => Validators.phone(value, "phNumber"), + readOnly: true, + txtwidth: ResponsiveLayout.isMobile(context) + ? null + : MediaQuery.of(context).size.width * 0.26, + ), + ], + ); + } + + Widget insurer(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('Insurer*', style: _textStyle), + SizedBox(height: 10), + ThemedFormField( + controller: controllers['insurer']!, + readOnly: true, + txtwidth: ResponsiveLayout.isMobile(context) + ? null + : MediaQuery.of(context).size.width * 0.26, + ), + ], + ); + } + + Widget buildIdv(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('IDV *', style: _textStyle), + SizedBox(height: 10), + ThemedFormField( + controller: controllers['idv']!, + readOnly: true, + txtwidth: ResponsiveLayout.isMobile(context) + ? null + : MediaQuery.of(context).size.width * 0.26, + ), + ], + ); + } + + Widget buildPremiumAmnt(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('Premium Amount *', style: _textStyle), + SizedBox(height: 10), + ThemedFormField( + controller: controllers['premium_amount']!, + readOnly: true, + validator: (value) => + Validators.doubleNumber(value, "Premium Amount"), + txtwidth: ResponsiveLayout.isMobile(context) + ? null + : MediaQuery.of(context).size.width * 0.26, + ), + ], + ); + } + + Widget buildPlanType(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('Plan Type *', style: _textStyle), + SizedBox(height: 10), + ThemedFormField( + controller: controllers['planType']!, + readOnly: true, + txtwidth: ResponsiveLayout.isMobile(context) + ? null + : MediaQuery.of(context).size.width * 0.26, + ), + ], + ); + } + + Widget Insuredname(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('Insured Name *', style: _textStyle), + SizedBox(height: 10), + ThemedFormField( + controller: controllers['policyInsName']!, + validator: (value) => + Validators.requiredField(value, "policyInsName"), + txtwidth: ResponsiveLayout.isMobile(context) + ? null + : MediaQuery.of(context).size.width * 0.26, + ), + ], + ); + } + + Widget premiumamount(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('Premium Amount ', style: _textStyle), + SizedBox(height: 10), + ThemedFormField( + controller: controllers['premium_amount']!, + readOnly: true, + // validator: (value) => + // Validators.requiredField(value, "policyPremiumAmount"), + txtwidth: ResponsiveLayout.isMobile(context) + ? null + : MediaQuery.of(context).size.width * 0.26, + ), + ], + ); + } + + Widget policynumber(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('Policy Number *', style: _textStyle), + SizedBox(height: 10), + ThemedFormField( + controller: controllers['policyNumber']!, + validator: (value) => Validators.requiredField(value, "policyNumber"), + txtwidth: ResponsiveLayout.isMobile(context) + ? null + : MediaQuery.of(context).size.width * 0.26, + ), + ], + ); + } + + Widget Paymentmode(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('Payment Mode *', style: _textStyle), + SizedBox(height: 10), + ThemedFormField( + controller: controllers['policyPaymentMode']!, + validator: (value) => + Validators.requiredField(value, "policyPaymentMode"), + txtwidth: ResponsiveLayout.isMobile(context) + ? null + : MediaQuery.of(context).size.width * 0.26, + ), + ], + ); + } + + Widget policyissuedate(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('Issue Date *', style: _textStyle), + SizedBox(height: 10), + ThemedDateField( + hintText: "Select Date", + txtwidth: ResponsiveLayout.isMobile(context) + ? null + : MediaQuery.of(context).size.width * 0.26, + txtheight: 50, + // backgroundColor: const Color(0xFFECECEC), + validator: (value) => Validators.requiredField(value, "date"), + controller: controllers['issueDate']!, + onDateSelected: (date) { + print("Picked Date: $date"); + + controllers['issueDate']?.text = DateFormat( + 'dd-MM-yyyy', + ).format(date); + // controllers['date']?.text = date as String; + }, + ), + ], + ); + } + + Widget policystartdate(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('Start Date *', style: _textStyle), + SizedBox(height: 10), + ThemedDateField( + hintText: "Select Date", + txtwidth: ResponsiveLayout.isMobile(context) + ? null + : MediaQuery.of(context).size.width * 0.26, + txtheight: 50, + // backgroundColor: const Color(0xFFECECEC), + validator: (value) => Validators.requiredField(value, "date"), + controller: controllers['startDate']!, + onDateSelected: (date) { + print("Picked Date: $date"); + + controllers['startDate']?.text = DateFormat( + 'dd-MM-yyyy', + ).format(date); + + // auto set end date (1 year later - 1 day if needed) + final endDate = DateTime( + date.year + 1, + date.month, + date.day, + ).subtract(const Duration(days: 1)); // optional: subtract 1 day + + controllers['endDate']?.text = DateFormat( + 'dd-MM-yyyy', + ).format(endDate); + + print("Auto-set End Date: $endDate"); + // controllers['date']?.text = date as String; + }, + ), + ], + ); + } + + Widget policyenddate(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('End Date *', style: _textStyle), + SizedBox(height: 10), + ThemedDateField( + hintText: "Select Date", + txtwidth: ResponsiveLayout.isMobile(context) + ? null + : MediaQuery.of(context).size.width * 0.26, + txtheight: 50, + // backgroundColor: const Color(0xFFECECEC), + // validator: (value) => Validators.requiredField(value, "date"), + validator: (value) { + if (value == null || value.isEmpty) { + return "Required"; + } + + final startDateControllerText = controllers['startDate']?.text; + if (startDateControllerText == null || + startDateControllerText.isEmpty) { + return "Select start date first"; + } + + final startDate = DateFormat( + 'dd-MM-yyyy', + ).parse(startDateControllerText); + final endDate = DateFormat('dd-MM-yyyy').parse(value); + + if (endDate.isBefore(startDate)) { + return "End date cannot be before start date"; + } + + // Minimum end date = start date + 1 year - 1 day (or just +1 year) + final minEndDate = DateTime( + startDate.year + 1, + startDate.month, + startDate.day, + ).subtract(const Duration(days: 1)); + + if (endDate.isBefore(minEndDate)) { + return "End date must be at least 1 year from start date"; + } + + return null; + + // final startDateControllerText = controllers['startDate']?.text; + // if (startDateControllerText == null || + // startDateControllerText.isEmpty) { + // return "Select start date first"; + // } + // + // final startDate = DateFormat( + // 'dd-MM-yyyy', + // ).parse(startDateControllerText); + // final endDate = DateFormat('dd-MM-yyyy').parse(value); + // + // if (endDate.isBefore(startDate)) { + // return "End date cannot be before start date"; + // } + // + // final expectedEndDate = DateTime( + // startDate.year + 1, + // startDate.month, + // startDate.day, + // ).subtract(const Duration(days: 1)); + // + // if (endDate != expectedEndDate) { + // return "End date must be exactly 1 year from start date"; + // } + // + // return null; // valid + }, + controller: controllers['endDate']!, + onDateSelected: (date) { + print("Picked Date: $date"); + + controllers['endDate']?.text = DateFormat( + 'dd-MM-yyyy', + ).format(date); + _formKey.currentState?.validate(); + // controllers['date']?.text = date as String; + }, + ), + ], + ); + } + + Widget buildUploadPolicyPdf(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Text('Upload Policy PDF * ', style: _textStyle), + const SizedBox(height: 10), + Row( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + ThemedUploadField( + hintText: selectedPDFFileNames ?? "Upload Document", + txtwidth: ResponsiveLayout.isMobile(context) + ? null + : MediaQuery.of(context).size.width * 0.13, + // backgroundColor: Color(0xFFEDF6F5), + onFileSelected: (fileName, file) { + print("Picked file: $fileName (${file.size} bytes)"); + setState(() { + docPDFUploadedFile = file; + }); + }, + ), + const SizedBox(width: 10), + + if (docUploadedPDFFileUrlFromApi != null) + Tooltip( + message: 'Download', + // color: Colors.white, + child: + // Row( + // mainAxisSize: MainAxisSize.min, + // mainAxisAlignment: MainAxisAlignment.end, + // children: [ + InkWell( + onTap: () => apiService.downloadFile( + apiUrl: + 'api/policy/downloadPolicyFile?policy_id=$choosedPolcyId&file_type=policy_pdf', + apiId: choosedPolcyId, + localFile: docPDFUploadedFile, + fileName: selectedPDFFileNames, + ), + + child: Container( + padding: const EdgeInsets.symmetric( + horizontal: 5, + vertical: 8, + ), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(5), + color: Color(0xFF425B5B), + // color: Colors.green.shade300, + ), + child: Row( + children: const [ + // Text( + // "Download", + // style: TextStyle( + // fontSize: 12, + // fontWeight: FontWeight.w200, + // color: Colors.white, + // ), + // ), + // SizedBox(width: 5), + Icon(Icons.download, size: 13, color: Colors.white), + ], + ), + ), + ), + // ], + // ), + ), + ], + ), + ], + ); + } + + Widget buildUploadPolicyReceipt(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('Upload Policy Receipt *', style: _textStyle), + const SizedBox(height: 10), + Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + ThemedUploadField( + hintText: selectedFileNames ?? "Upload Document", + txtwidth: ResponsiveLayout.isMobile(context) + ? null + : MediaQuery.of(context).size.width * 0.26, + // backgroundColor: Color(0xFFEDF6F5), + onFileSelected: (fileName, file) { + print("Picked file: $fileName (${file.size} bytes)"); + setState(() { + docUploadedFile = file; + }); + }, + ), + const SizedBox(height: 5), + + if (docUploadedFileUrlFromApi != null) + Container( + // color: Colors.white, + child: Row( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.end, + children: [ + GestureDetector( + onTap: () => apiService.downloadFile( + apiUrl: + 'api/policy/downloadPolicyFile?policy_id=$choosedPolcyId&file_type=policy_payment_receipt', + apiId: choosedPolcyId, + localFile: docUploadedFile, + fileName: selectedFileNames, + ), + child: Container( + padding: const EdgeInsets.all(5), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(5), + color: Color(0xFF425B5B), + // color: Colors.green.shade300, + ), + child: Row( + children: const [ + Text( + "Download", + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.w200, + color: Colors.white, + ), + ), + SizedBox(width: 5), + Icon(Icons.download, size: 13, color: Colors.white), + ], + ), + ), + ), + ], + ), + ), + ], + ), + ], + ); + } + + Widget buildResponsiveUploadField({ + String? label, // 🔹 make optional + required String? hintText, + required void Function(String fileName, dynamic file) onFileSelected, + }) { + final isMobile = ResponsiveLayout.isMobile(context); + + final uploadWidget = ThemedUploadField( + hintText: hintText ?? "Upload Documents", + txtwidth: isMobile ? null : MediaQuery.of(context).size.width * 0.26, + onFileSelected: onFileSelected, + ); + + if (isMobile) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (label != null && label.isNotEmpty) ...[ + // ✅ only render if label is given + Text(label, style: _textStyle), + const SizedBox(height: 8), + ], + uploadWidget, + const SizedBox(height: 16), + ], + ); + } else { + return Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (label != null && label.isNotEmpty) ...[ + Expanded(child: Text(label, style: _textStyle)), + const SizedBox(width: 10), + ], + uploadWidget, + ], + ); + } + } + + Widget buildResponsiveField({required String label, required Widget field}) { + final isMobile = ResponsiveLayout.isMobile(context); + + if (isMobile) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(label, style: _textStyle), + const SizedBox(height: 8), + field, + const SizedBox(height: 16), + ], + ); + } else { + return Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded(child: Text(label, style: _textStyle)), + const SizedBox(width: 10), + field, + ], + ); + } + } + + static const _textStyle = TextStyle( + fontSize: 12, + fontWeight: FontWeight.w600, + ); + static const _textStrongStyle = TextStyle( + fontSize: 12, + fontWeight: FontWeight.w800, + ); +} diff --git a/lib/presentation/screens/staff/Enquiry/tabs/quotationTab.dart b/lib/presentation/screens/staff/Enquiry/tabs/quotationTab.dart new file mode 100644 index 0000000..8c3123b --- /dev/null +++ b/lib/presentation/screens/staff/Enquiry/tabs/quotationTab.dart @@ -0,0 +1,723 @@ +import 'dart:io' as html; + +import 'package:dropdown_search/dropdown_search.dart'; +import 'package:file_picker/file_picker.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/foundation.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:shared_preferences/shared_preferences.dart'; + +import '../../../../../core/routing/routes.dart'; +import '../../../../../core/services/api_service.dart'; +import '../../../../../data/services/auth_service.dart'; +import '../../../../../data/utils/validators.dart'; +import '../../../../layouts/main_layout.dart'; +import '../../../../layouts/responsive_layout.dart'; +import '../../../../providers/manager_provider.dart'; +import '../../../../providers/quotation_staff_proivder.dart'; +import '../../../../themes/indicators/text_field_theme.dart'; +import '../../quotations/createQuotationPopUp.dart'; +import 'createQuotation.dart'; + +class QuotationStaffTab extends ConsumerStatefulWidget { + String? id; + List>? data; + final Future Function()? onRefresh; + + QuotationStaffTab({super.key, this.data, this.id, this.onRefresh}); + @override + ConsumerState createState() => QuotationStaffTabState(); +} + +class QuotationStaffTabState extends ConsumerState { + int currentPage = 0; + int itemsPerPage = 10; + + bool blockKey = false; + + List> dataVal = []; + + final TextEditingController _searchController = TextEditingController(); + + late ApiService apiService; + final _formKey = GlobalKey(); + final GlobalKey>> dropDownKey = + GlobalKey>>(); + + List tabHeader = [ + 'regNum', + 'insurer', + 'mobile', + 'code', + 'address', + 'regNo', + ]; + late String isActive = "1"; + PlatformFile? docUploadedFile; + + // PlatformFile? passportFile; + String? selectedFileNames; + String? selectedInsurdId; + String? passportFileUrlFromApi; + String? selectedId; + bool isLoading = false; + bool isquotation = false; + + Map controllers = {}; + String? _token; + dynamic userId; + dynamic selectedEnquiryId; + dynamic managerId; + dynamic enqQuotation; + dynamic selectedQuotationFrmListId; + + Map? selectedQuotationFrmListData; + + List> getVehicleTypeData = []; + List> filteredVechicleData = []; + + List> getInsurersData = []; + List> filteredInsurersData = []; + + Map dataDetails() { + final data = { + "regNum": controllers["regNum"]?.text, + // "insurer": controllers["insurer"]?.text, + "insurer": controllers["insurer"]?.text, + "mobile": controllers["mobile"]?.text, + "address": controllers["address"]?.text, + "agent_code": controllers["code"]?.text, + "is_active": isActive, + "manager_id": managerId, + }; + return data; + } + + List> getQuotationData = []; + late Map enquiryData; + List> originalData = []; + List> filteredData = []; + // bool isLoading = false; + + Future _restoreManagerId(WidgetRef ref) async { + final prefs = await SharedPreferences.getInstance(); + + final String? savedId = prefs.getString( + 'enqStaffDataId', + ); // already a string + if (savedId != null) { + ref.read(quotationStaffIdProvider.notifier).state = savedId; + print("savedenqStaffDataId ID restored: $savedId"); + + _loadData(savedId); + } + } + + @override + void initState() { + super.initState(); + apiService = ApiService(); + + if (kIsWeb) { + Future.microtask(() => _restoreManagerId(ref)); + } + + for (String field in tabHeader) { + controllers[field] = TextEditingController(); + } + + _initializeToken(); + + Future.microtask(() { + managerId = ref.read(managerIdProvider); // ✅ use read + userId = ref.read(userIdProvider); // ✅ use read + + print('QmanagerId - $managerId'); + print('QuserId - $userId'); + enqQuotation = ref.read(quotationStaffIdProvider); // ✅ use read + print('QenqQuotation - $enqQuotation'); + if (enqQuotation != null) { + _loadData(enqQuotation); + } + }); + } + + @override + void dispose() { + // Dispose all TextEditingControllers + for (var controller in controllers.values) { + controller.dispose(); + } + super.dispose(); + } + + Future _initializeToken() async { + _token = await AuthService.getToken(); + print("APISERTOKEN - $_token"); + } + + void updateEnquiryData(enquiryData) { + print('updateEnquiryData - $enquiryData'); + setState(() { + selectedInsurdId = enquiryData['insurer_id']; + selectedEnquiryId = enquiryData['id']; + print('selectedEnquiryId- $selectedEnquiryId'); + controllers["regNum"]?.text = enquiryData['reg_no']; + // controllers["insurer"]?.text = enquiryData['insurer_name']; + }); + } + + Future _loadData(enqQuotation) async { + getQuotationList(enqQuotation); + } + + void refresh() { + print("refresh--"); + if (enqQuotation != null) { + _loadData(enqQuotation); + } + } + + Future getQuotationList(enqQuotation) async { + print('getClaimList called enqQuotation- $enqQuotation'); + setState(() { + isLoading = true; + }); + + try { + final response = await apiService.findEnqQuotePolicyView(enqQuotation); + + if (response['status'] == 'success') { + print('quoationListData - ${response['data']}'); + setState(() { + enquiryData = Map.from(response['data']['enquiry']); + print('enquiryData - ${enquiryData}'); + + updateEnquiryData(enquiryData); + // getQuotationData = List>.from(response['data']); + getQuotationData = List>.from( + response['data']['quotations'], + ); + originalData = getQuotationData; + filteredData = List.from(originalData); + + blockKey = getQuotationData.any( + (item) => item['status'] == 'Accepted', + ); + print('blockKey- $blockKey'); + // print('originalData - $getClaimPolicies'); + }); + } else { + getQuotationData = []; + originalData = []; + } + } catch (e) { + print('Exception occurred: $e'); + } finally { + setState(() { + isLoading = false; + }); + } + } + + Color _getStatusColor(String? status) { + switch (status) { + case 'Pending': + return Colors.yellow; + case 'Accepted': + return Colors.green; + case 'Rejected': + return Colors.red; + default: + return Colors.grey; + } + } + + void handleEdit(id, data) { + print("sfd - $id"); + print("sfdewe - $data"); + setState(() { + selectedQuotationFrmListId = id; + selectedQuotationFrmListData = data; + // data = data; + }); + } + + @override + Widget build(BuildContext context) { + final isMobile = ResponsiveLayout.isMobile(context); + + return Container( + height: MediaQuery.of(context).size.height, + width: MediaQuery.of(context).size.width, + // child: SingleChildScrollView( + // scrollDirection: Axis.vertical, + child: Column( + children: [ + if (!blockKey) ...[ + CreateQuotationForm( + key: ValueKey(selectedQuotationFrmListId ?? "new"), + userId: userId, + selectedQuotationFrmListdata: selectedQuotationFrmListData, + selectedQuotationFrmListId: selectedQuotationFrmListId, + managerId: managerId, + selectedInsurdId: selectedInsurdId, + selectedEnquiryId: selectedEnquiryId, + onSubmit: (value) { + debugPrint("New Endorsement: $value"); + if (value == 'Success') { + refresh(); + } + }, + ), + SizedBox(height: 20), + ], + + //(TABLE) + Container( + // color: Colors.green, + decoration: BoxDecoration( + color: Color(0xFFEDF6F5), + borderRadius: BorderRadius.circular(10.0), + ), + width: MediaQuery.of(context).size.width, + height: !blockKey + ? MediaQuery.of(context).size.height * 0.44 + : MediaQuery.of(context).size.height * 0.6, + padding: EdgeInsets.all(12.0), + child: SingleChildScrollView( + scrollDirection: Axis.vertical, + child: Column( + children: [ + // Text('datta') + _buildDataTable(context), + ], + ), + ), + ), + ], + ), + // ), + ); + } + + Widget registerNumber(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('Registration Number', style: _textStyle), + SizedBox(height: 10), + ThemedFormField( + controller: controllers['regNum']!, + readOnly: true, + txtwidth: ResponsiveLayout.isMobile(context) + ? null + : MediaQuery.of(context).size.width * 0.2, + ), + ], + ); + } + + Widget insurer(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('Insurer', style: _textStyle), + SizedBox(height: 10), + ThemedFormField( + controller: controllers['insurer']!, + readOnly: true, + txtwidth: ResponsiveLayout.isMobile(context) + ? null + : MediaQuery.of(context).size.width * 0.2, + ), + ], + ); + } + + Widget buildResponsiveField({required String label, required Widget field}) { + final isMobile = ResponsiveLayout.isMobile(context); + + if (isMobile) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(label, style: _textStyle), + const SizedBox(height: 8), + field, + const SizedBox(height: 16), + ], + ); + } else { + return Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded(child: Text(label, style: _textStyle)), + const SizedBox(width: 10), + field, + ], + ); + } + } + + Widget buildId(BuildContext context) { + return buildResponsiveField( + label: "Registration Number *", + field: ThemedFormField( + controller: controllers['regNo']!, + validator: (value) => Validators.requiredField(value, "regNo"), + txtwidth: ResponsiveLayout.isMobile(context) + ? null + : MediaQuery.of(context).size.width * 0.26, + ), + ); + } + + Widget _buildDataTable(BuildContext context) { + if (filteredData.isEmpty) { + return const SizedBox( + height: 50, + child: Center(child: Text('No available data')), + ); + } + + final sortedData = [...filteredData] + ..sort((a, b) => int.parse(b['id']) - int.parse(a['id'])); + + return SingleChildScrollView( + child: Column( + children: [ + ListView.builder( + shrinkWrap: true, + physics: NeverScrollableScrollPhysics(), + 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) + // ? _buildDataCard(item, sno) + return _buildDataRow(item, sno); + }, + ), + ], + ), + ); + } + + Widget _buildHeader() { + return Container( + decoration: BoxDecoration( + color: Color(0xFFEDF6F5), + borderRadius: BorderRadius.circular(6), + ), + padding: const EdgeInsets.symmetric(vertical: 3, horizontal: 3), + child: Row( + children: [ + // Expanded(flex: 3, child: Text(' ', style: _headerStyle)), + Expanded( + flex: 2, + child: Text('Registration Number', style: _headerStyle), + ), + Expanded(flex: 3, child: Text('Insurer', style: _headerStyle)), + Expanded(flex: 2, child: Text('IDV', style: _headerStyle)), + Expanded(flex: 2, child: Text('Plan Type', style: _headerStyle)), + Expanded(flex: 2, child: Text('Premium Amount', style: _headerStyle)), + Expanded(flex: 2, child: Text('Status', style: _headerStyle)), + Expanded(flex: 1, child: Text('', style: _headerStyle)), + ], + ), + ); + } + + Widget _buildDataRow(Map item, sno) { + return Container( + padding: const EdgeInsets.symmetric(vertical: 6, horizontal: 16), + margin: const EdgeInsets.only(top: 10), + decoration: BoxDecoration( + color: Colors.white, + // color: Color(0xFFE0F7F9), + border: const Border( + bottom: BorderSide(color: Color(0xFFEAEAEA), width: 1), + ), + borderRadius: BorderRadius.circular(8), + ), + child: Row( + children: [ + Expanded( + flex: 2, + child: Text(item['reg_no'] ?? '-', style: _dataBold), + ), + Expanded( + flex: 3, + child: Text(item['insurer_name'] ?? '-', style: _dataBold), + ), + Expanded( + flex: 2, + child: Text( + item['insured_declared_value'] ?? '-', + style: _dataBold, + ), + ), + Expanded( + flex: 2, + child: Text(item['insurance_plan_type'] ?? '-', style: _dataBold), + ), + Expanded( + flex: 2, + child: Text(item['premium_amount'] ?? '-', style: _dataBold), + ), + + Expanded( + flex: 2, + child: Text( + item['status'] ?? '-', + style: GoogleFonts.inter( + fontSize: 14, + fontWeight: FontWeight.w400, + color: _getStatusColor(item['status']), + ), + ), + ), + + Expanded( + flex: 1, + child: item['status'] == 'Pending' + ? Tooltip( + message: 'Edit', + child: IconButton( + icon: Image.asset( + "assets/miscellaneous/Edit.png", + height: 15, + width: 15, + ), + onPressed: () { + final id = item['id']; + print('RAV - $id'); + handleEdit(id, item); + }, + splashRadius: 20, + hoverColor: Colors.black12, + padding: const EdgeInsets.all(8), + constraints: const BoxConstraints(), + ), + ) + : Image.asset( + "assets/miscellaneous/Edit_muted.png", + height: 15, + width: 15, + ), + // child: GestureDetector( + // onTap: () { + // final id = item['id']; + // print('RAV - $id'); + // handleEdit(id, item); + // }, + // child: item['status'] == 'Pending' + // ? Image.asset( + // "assets/miscellaneous/Edit.png", + // height: 15, + // width: 15, + // ) + // : Image.asset( + // "assets/miscellaneous/Edit_muted.png", + // height: 15, + // width: 15, + // ), + // ), + ), + ], + ), + ); + } + + 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(0xFFFFFFFF), + // color: const Color(0xFFF6FEFD), + borderRadius: BorderRadius.circular(8.0), + border: Border.all(color: const Color(0xffD9EBE8)), + ), + child: Row( + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + // "TN64V3456", + item['reg_no'] ?? '-', + style: _cardheaderStyle, + ), + item['status'] == 'Pending' + // ? GestureDetector( + // onTap: () { + // final id = item['id']; + // print('RAV - $id'); + // handleEdit(id, item); + // }, + // child: Image.asset( + // "assets/miscellaneous/Edit.png", + // height: 15, + // width: 15, + // ), + // ) + ? Tooltip( + message: 'Edit', + child: IconButton( + icon: Image.asset( + "assets/miscellaneous/Edit.png", + height: 15, + width: 15, + ), + onPressed: () { + final id = item['id']; + print('RAV - $id'); + // handleEdit(id, item); + }, + splashRadius: 28, + hoverColor: Colors.black12, + padding: const EdgeInsets.all(8), + constraints: const BoxConstraints(), + ), + ) + : Image.asset( + "assets/miscellaneous/Edit_muted.png", + height: 15, + width: 15, + ), + ], + ), + Row( + // mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + // flex: 2, + child: Text( + item['insurer_name'] ?? '-', + style: _cardRow1BodyStyle, + ), + ), + ], + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + item['insurance_plan_type'] ?? '-', + style: _cardRow1BodyStyle, + ), + + Text( + 'IDV: ${item['insured_declared_value']}' ?? '-', + style: _cardRow2BodyStyle, + ), + ], + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + RichText( + text: TextSpan( + children: [ + TextSpan( + text: 'Status : ', // key + style: GoogleFonts.inter( + color: Colors.black, + fontWeight: FontWeight.w600, + fontSize: 11, + ), // key color + ), + TextSpan( + text: '${item['status'] ?? '-'}', // value + style: GoogleFonts.inter( + color: _getStatusColor(item['status']), + fontWeight: FontWeight.w600, + fontSize: 11, + ), // value color + ), + ], + ), + ), + + Text( + '${item['premium_amount']}' ?? '-', + style: _cardheaderStyle, + ), + ], + ), + ], + ), + ), + ], + ), + ); + } + + static final _dataBold = GoogleFonts.inter( + fontSize: 12, + + fontWeight: FontWeight.w400, + color: Color(0xFF000000), + ); + + static final _dataSub = GoogleFonts.inter( + fontSize: 10, + fontWeight: FontWeight.w300, + color: Color(0xFF585757), + ); + + static final _headerStyle = GoogleFonts.inter( + color: Colors.black, + fontWeight: FontWeight.w600, + fontSize: 13, + ); + + static final _textStyle = GoogleFonts.inter( + fontSize: 14, + fontWeight: FontWeight.w600, + ); + + static final _cardheaderStyle = GoogleFonts.inter( + color: Colors.black, + fontWeight: FontWeight.w600, + fontSize: 12, + ); + + static final _cardBodyStyle = GoogleFonts.inter( + color: Color(0xFF545454), + fontWeight: FontWeight.w400, + fontSize: 12, + ); + + static final _cardRow1BodyStyle = GoogleFonts.inter( + // color: Color(0xFF545454), + color: Colors.black, + fontWeight: FontWeight.w500, + fontSize: 12, + ); + static final _cardRow2BodyStyle = GoogleFonts.inter( + color: const Color(0xFF545454), + // color: Colors.black, + fontWeight: FontWeight.w500, + fontSize: 12, + ); +} diff --git a/lib/presentation/screens/staff/Enquiry/tabs/tab.dart b/lib/presentation/screens/staff/Enquiry/tabs/tab.dart new file mode 100644 index 0000000..9759fb3 --- /dev/null +++ b/lib/presentation/screens/staff/Enquiry/tabs/tab.dart @@ -0,0 +1,214 @@ +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:go_router/go_router.dart'; +import 'package:google_fonts/google_fonts.dart'; +import 'package:nhance_partner/presentation/providers/userRoleProvider.dart'; +import 'package:nhance_partner/presentation/screens/staff/Enquiry/tabs/policyTab.dart'; +import 'package:nhance_partner/presentation/screens/staff/Enquiry/tabs/quotationTab.dart'; +import 'package:shared_preferences/shared_preferences.dart'; + +import '../../../../../core/routing/routes.dart'; +import '../../../../../core/services/api_service.dart'; +import '../../../../layouts/main_layout.dart'; +import '../../../../providers/manager_provider.dart'; +import '../../../../providers/quotation_staff_proivder.dart'; + +class TabEnquiryStaffList extends ConsumerStatefulWidget { + String? id; + String? showKey; + + TabEnquiryStaffList({super.key, this.id, this.showKey}); + @override + ConsumerState createState() => + TabEnquiryStaffListState(); +} + +class TabEnquiryStaffListState extends ConsumerState { + int? expandedIndex; + int selectedIndex = 0; + List tabs = []; + Map? enquiryData; + bool isLoading = true; + late ApiService apiService; + ScrollController _scrollController = ScrollController(); + dynamic role; + + @override + void initState() { + super.initState(); + apiService = ApiService(); + + if (kIsWeb) { + Future.microtask(() => _restoreId(ref)); + } + + Future.microtask(() { + final id = ref.read(quotationStaffIdProvider); + role = ref.read(userRoleProvider); + print("IntialID - $id"); + if (id != null) { + _loadData(id); + } else { + setState(() => isLoading = false); + tabs = [ + TabItem("Proposal", QuotationStaffTab()), + // TabItem("Policy", PolicyStaffTab()), + ]; + } + }); + // fetch immediately + } + + Future _restoreId(WidgetRef ref) async { + final prefs = await SharedPreferences.getInstance(); + + final String? savedId = prefs.getString( + 'enqAgentDataId', + ); // already a string + if (savedId != null) { + ref.read(enquiryIdProvider.notifier).state = savedId; + print("savedenqStaffDataId ID restored: $savedId"); + + _loadData(savedId); + } + } + + Future _loadData(id, {int? tabIndex}) async { + print('loadQuotationTab 2'); + setState(() => isLoading = true); + // final response = await apiService.findEnqQuotePolicyView(id); + // print('loadQuotationTab 3 '); + + setState(() { + print('loadQuotationTab 4'); + // enquiryData = response["data"]; + isLoading = false; + tabs = [ + TabItem("Proposal", QuotationStaffTab()), + // TabItem("Policy", PolicyStaffTab()), + ]; + + if (widget.showKey == "Policy") { + tabs.add(TabItem("Policy", PolicyStaffTab())); + } + + // if (tabIndex != null) { + // selectedIndex = tabIndex; + // expandedIndex = tabIndex; + // } + + // Auto-select tab based on showKey + if (widget.showKey == "Policy") { + selectedIndex = 1; // Policy tab index + } else if (tabIndex != null) { + selectedIndex = tabIndex; + } else { + selectedIndex = 0; // Default Quotation tab + } + + expandedIndex = selectedIndex; + }); + } + + /// Public method to load data and select a tab + Future loadQuotationTab(String id) async { + print('loadQuotationTab 1'); + await _loadData(id, tabIndex: 2); // now _loadData will set the selected tab + } + + @override + Widget build(BuildContext context) { + final bool isMobile = MediaQuery.of(context).size.width < 600; + // final id = ref.watch(enquiryIdProvider); + // + // print("TABUPDID- $id"); + // + // print('ENQID: $id'); + + return AlertDialog( + backgroundColor: Colors.white, + content: Container( + width: MediaQuery.of(context).size.width * 0.55, + height: MediaQuery.of(context).size.height * 0.7, + + child: isLoading + ? const Center(child: CircularProgressIndicator()) + : tabs.isEmpty + ? const Center( + child: Text( + "No data available", + style: TextStyle(fontSize: 16, color: Colors.black54), + ), + ) + : _buildDesktopTabs(), + ), + ); + } + + Widget _buildDesktopTabs() { + return Column( + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Row( + children: List.generate(tabs.length, (index) { + final isSelected = selectedIndex == index; + return Padding( + padding: const EdgeInsets.only(left: 10.0), + child: ElevatedButton( + style: ElevatedButton.styleFrom( + elevation: 2, + backgroundColor: isSelected + ? const Color(0xFF425B5B) + : const Color(0xFFEDFFFC), + foregroundColor: isSelected + ? Colors.white + : Colors.black87, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(6), + ), + padding: const EdgeInsets.symmetric( + horizontal: 24, + vertical: 12, + ), + ), + onPressed: () { + setState(() => selectedIndex = index); + }, + child: Text(tabs[index].title), + ), + ); + }), + ), + + GestureDetector( + onTap: () => Navigator.pop(context), + child: Container( + padding: const EdgeInsets.all(5.0), + decoration: BoxDecoration( + color: const Color(0xFFF1F1F1), + borderRadius: BorderRadius.circular(5.0), + ), + child: Tooltip( + message: 'Close', + child: const Icon(Icons.close, size: 18), + ), + ), + ), + ], + ), + + SizedBox(height: 10), + Expanded(child: tabs[selectedIndex].widget), + ], + ); + } +} + +class TabItem { + final String title; + final Widget widget; + TabItem(this.title, this.widget); +} diff --git a/lib/presentation/screens/staff/assignStaff.dart b/lib/presentation/screens/staff/assignStaff.dart index 1fa285d..d9e8e75 100644 --- a/lib/presentation/screens/staff/assignStaff.dart +++ b/lib/presentation/screens/staff/assignStaff.dart @@ -5,6 +5,7 @@ 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:nhance_partner/presentation/providers/userRoleProvider.dart'; import 'package:toastification/toastification.dart'; @@ -27,7 +28,6 @@ class AssignStaffDialog extends ConsumerStatefulWidget { const AssignStaffDialog({ super.key, - required this.onSubmit, required this.regNum, required this.userId, @@ -42,7 +42,7 @@ class AssignStaffDialog extends ConsumerStatefulWidget { class _AddDialogState extends ConsumerState { late ApiService apiService; String? _token; - + dynamic role; bool isLoading = false; bool showSuccess = false; late TextEditingController controller; @@ -96,10 +96,13 @@ class _AddDialogState extends ConsumerState { getInsurers(); Future.microtask(() { final managerId = ref.watch(managerIdProvider); - final handlerId = ref.watch(handlerIdProvider); - if (handlerId != null) { + final userID = ref.watch(userIdProvider); + // final handlerId = ref.watch(handlerIdProvider); + role = ref.watch(userRoleProvider); + print("managerId - $managerId"); + if (userID != null) { print('hansles'); - getStaffDetails(handlerId); + getStaffDetails(userID); } }); } @@ -141,17 +144,15 @@ class _AddDialogState extends ConsumerState { } Future getStaffDetails(int id) async { - print('getStaffDetails called'); + print('getStaffDetails called By handler'); setState(() { isLoading = true; }); try { - final response = await apiService.fetchStaffListForEnquiryAssignDropDown( - id, - ); + final response = await apiService.fetchStaffUserList(id, role); - if (response['status'] == 200) { + if (response['status'] == 'success') { print('getStaffDetails - ${response['data']}'); setState(() { getStaffDetailsData = List>.from( diff --git a/lib/presentation/screens/staff/policy/policy.dart b/lib/presentation/screens/staff/policy/policy.dart index be821e1..ad87971 100644 --- a/lib/presentation/screens/staff/policy/policy.dart +++ b/lib/presentation/screens/staff/policy/policy.dart @@ -1222,7 +1222,7 @@ class PolicyScreenState extends ConsumerState { date.year + 1, date.month, date.day, - ).subtract(const Duration(days: 1)); // optional: subtract 1 day + ); // optional: subtract 1 day controllers['endDate']?.text = DateFormat( 'dd-MM-yyyy', @@ -1250,64 +1250,97 @@ class PolicyScreenState extends ConsumerState { txtheight: 50, // backgroundColor: const Color(0xFFECECEC), // validator: (value) => Validators.requiredField(value, "date"), + // validator: (value) { + // if (value == null || value.isEmpty) { + // return "Required"; + // } + // + // final startDateControllerText = controllers['startDate']?.text; + // if (startDateControllerText == null || + // startDateControllerText.isEmpty) { + // return "Select start date first"; + // } + // + // final startDate = DateFormat( + // 'dd-MM-yyyy', + // ).parse(startDateControllerText); + // final endDate = DateFormat('dd-MM-yyyy').parse(value); + // + // if (endDate.isBefore(startDate)) { + // return "End date cannot be before start date"; + // } + // + // // Minimum end date = start date + 1 year - 1 day (or just +1 year) + // final minEndDate = DateTime( + // startDate.year + 1, + // startDate.month, + // startDate.day, + // ).subtract(const Duration(days: 1)); + // + // if (endDate.isBefore(minEndDate)) { + // return "End date must be at least 1 year from start date"; + // } + // + // return null; + // + // // final startDateControllerText = controllers['startDate']?.text; + // // if (startDateControllerText == null || + // // startDateControllerText.isEmpty) { + // // return "Select start date first"; + // // } + // // + // // final startDate = DateFormat( + // // 'dd-MM-yyyy', + // // ).parse(startDateControllerText); + // // final endDate = DateFormat('dd-MM-yyyy').parse(value); + // // + // // if (endDate.isBefore(startDate)) { + // // return "End date cannot be before start date"; + // // } + // // + // // final expectedEndDate = DateTime( + // // startDate.year + 1, + // // startDate.month, + // // startDate.day, + // // ).subtract(const Duration(days: 1)); + // // + // // if (endDate != expectedEndDate) { + // // return "End date must be exactly 1 year from start date"; + // // } + // // + // // return null; // valid + // }, validator: (value) { if (value == null || value.isEmpty) { return "Required"; } - final startDateControllerText = controllers['startDate']?.text; - if (startDateControllerText == null || - startDateControllerText.isEmpty) { + final startText = controllers['startDate']?.text; + if (startText == null || startText.isEmpty) { return "Select start date first"; } - final startDate = DateFormat('dd-MM-yyyy').parse(startDateControllerText); - final endDate = DateFormat('dd-MM-yyyy').parse(value); + final startDate = DateFormat('dd-MM-yyyy').parseStrict(startText); + final endDate = DateFormat('dd-MM-yyyy').parseStrict(value); if (endDate.isBefore(startDate)) { return "End date cannot be before start date"; } - // Minimum end date = start date + 1 year - 1 day (or just +1 year) - final minEndDate = DateTime( - startDate.year + 1, - startDate.month, - startDate.day, - ).subtract(const Duration(days: 1)); + // Add 365 days - 1 day to start date + final expectedEndDate = startDate + .add(const Duration(days: 365)) + .subtract(const Duration(days: 1)); - if (endDate.isBefore(minEndDate)) { - return "End date must be at least 1 year from start date"; + if (endDate.day != expectedEndDate.day || + endDate.month != expectedEndDate.month || + endDate.year != expectedEndDate.year) { + return "End date must be exactly 1 year minus 1 day from start date"; } return null; - - // final startDateControllerText = controllers['startDate']?.text; - // if (startDateControllerText == null || - // startDateControllerText.isEmpty) { - // return "Select start date first"; - // } - // - // final startDate = DateFormat( - // 'dd-MM-yyyy', - // ).parse(startDateControllerText); - // final endDate = DateFormat('dd-MM-yyyy').parse(value); - // - // if (endDate.isBefore(startDate)) { - // return "End date cannot be before start date"; - // } - // - // final expectedEndDate = DateTime( - // startDate.year + 1, - // startDate.month, - // startDate.day, - // ).subtract(const Duration(days: 1)); - // - // if (endDate != expectedEndDate) { - // return "End date must be exactly 1 year from start date"; - // } - // - // return null; // valid }, + controller: controllers['endDate']!, onDateSelected: (date) { print("Picked Date: $date"); diff --git a/lib/presentation/screens/staff/policy/policy_list.dart b/lib/presentation/screens/staff/policy/policy_list.dart index fd283a9..632910d 100644 --- a/lib/presentation/screens/staff/policy/policy_list.dart +++ b/lib/presentation/screens/staff/policy/policy_list.dart @@ -244,48 +244,47 @@ class policylistState extends ConsumerState { regNum, ) { return [ - if (roleId == 'manager') ...[ - Material( - color: Colors.transparent, - child: InkWell( - onTap: () { - Navigator.pop(context); - showDialog( - context: context, - builder: (ctx) => AssignStaffDialog( - enquiryPrimaryId: id, - regNum: regNum, - userId: userId, - 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') ...[ + // Material( + // color: Colors.transparent, + // child: InkWell( + // onTap: () { + // Navigator.pop(context); + // showDialog( + // context: context, + // builder: (ctx) => AssignStaffDialog( + // enquiryPrimaryId: id, + // regNum: regNum, + // userId: userId, + // 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( @@ -369,7 +368,7 @@ class policylistState extends ConsumerState { mainAxisAlignment: MainAxisAlignment.start, children: [ Container( - height: 40, + height: 30, width: MediaQuery.of(context).size.width, child: GestureDetector( onTap: () { @@ -409,7 +408,7 @@ class policylistState extends ConsumerState { ), ), - SizedBox(height: 10), + SizedBox(height: 5), ResponsiveLayout.isMobile(context) ? Container( @@ -617,7 +616,7 @@ class policylistState extends ConsumerState { ], ), ), - SizedBox(height: 10), + SizedBox(height: 5), if (!ResponsiveLayout.isMobile(context)) Container( decoration: BoxDecoration( @@ -627,14 +626,22 @@ class policylistState extends ConsumerState { padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 16), child: Row( children: [ - Expanded(flex: 4, child: Text('Date', style: _headerStyle)), - Expanded(flex: 3, child: Text('Reg.No', style: _headerStyle)), + Expanded( + flex: 4, + child: Text('Recieved Date/Time', style: _headerStyle), + ), + Expanded(flex: 3, child: Text('Partner', style: _headerStyle)), Expanded( flex: 3, child: Text('Assigned To', style: _headerStyle), ), - Expanded(flex: 3, child: Text('Insurer', style: _headerStyle)), + Expanded(flex: 4, child: Text('Insurer', style: _headerStyle)), + + Expanded( + flex: 3, + child: Text('Vehicle.No.', style: _headerStyle), + ), Expanded( flex: 3, child: Text('Insured Name', style: _headerStyle), @@ -758,10 +765,7 @@ class policylistState extends ConsumerState { maxLines: 3, ), ), - Expanded( - flex: 3, - child: Text(item['reg_no'] ?? '-', style: _dataBold), - ), + Expanded( flex: 3, child: Text( @@ -774,7 +778,7 @@ class policylistState extends ConsumerState { child: Text(item['assigned_to_name'] ?? '-', style: _dataBold), ), Expanded( - flex: 3, + flex: 4, child: Text( item['insurer_name'] ?? '-', style: _dataBold, @@ -782,6 +786,10 @@ class policylistState extends ConsumerState { maxLines: 3, ), ), + Expanded( + flex: 3, + child: Text(item['reg_no'] ?? '-', style: _dataBold), + ), Expanded( flex: 3, child: Text( @@ -873,7 +881,7 @@ class policylistState extends ConsumerState { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text("Company", style: _cardheaderStyle), + Text("Insurer", style: _cardheaderStyle), Text(item['insurer_name'] ?? '-', style: _cardBodyStyle), ], ), @@ -980,7 +988,7 @@ class policylistState extends ConsumerState { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text("Date", style: _cardheaderStyle), + Text("Recieved Date/Time", style: _cardheaderStyle), Text( _formatDate(item['updated_on']) ?? '-', style: _cardBodyStyle, diff --git a/lib/presentation/screens/staff/quotations/createQuotationPopUp.dart b/lib/presentation/screens/staff/quotations/createQuotationPopUp.dart index eb0e0c7..96caacd 100644 --- a/lib/presentation/screens/staff/quotations/createQuotationPopUp.dart +++ b/lib/presentation/screens/staff/quotations/createQuotationPopUp.dart @@ -78,7 +78,8 @@ class createQuotatDialogState extends State { final data = { "enquiry_id": widget.selectedEnquiryId, "insured_declared_value": controllers["idv"]?.text, - "insurer_id": selectedInsurer, + "insurer_id": '1', + // "insurer_id": selectedInsurer, "premium_amount": controllers["premium_Amount"]?.text, "insurance_plan_type_id": selectedInsPlanType, // "additional_uploaded_file_name": "extra_doc.pdf", @@ -135,6 +136,10 @@ class createQuotatDialogState extends State { .selectedQuotationFrmListdata!['insurance_plan_type_id'] ?.toString(); + selectedInsurer = '1'; + + // controllers["insurer"]?.text = 'LIC'; + // selectedInsurer = // widget.selectedQuotationFrmListdata!['insurer_id']?.toString() ?? ''; @@ -426,7 +431,7 @@ class createQuotatDialogState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ buildIdv(context), - buildInsurer(context), + // buildInsurer(context), buildInsurancePlanType(context), buildPremiumAmnt(context), buildDocuments(context), @@ -464,7 +469,7 @@ class createQuotatDialogState extends State { ); } - Widget buildInsurer(BuildContext context) { + Widget buildInsurer1(BuildContext context) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -474,7 +479,7 @@ class createQuotatDialogState extends State { controller: controllers['insurer']!, validator: (value) => Validators.requiredField(value, "insurer"), backgroundColor: Color(0xFFEDF6F5), - readOnly: true, + // readOnly: true, txtwidth: ResponsiveLayout.isMobile(context) ? null : MediaQuery.of(context).size.width * 0.26, @@ -483,96 +488,96 @@ 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) - // ? 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 buildInsurer2(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( diff --git a/lib/presentation/themes/indicators/text_field_theme.dart b/lib/presentation/themes/indicators/text_field_theme.dart index a761a42..d1ff67d 100644 --- a/lib/presentation/themes/indicators/text_field_theme.dart +++ b/lib/presentation/themes/indicators/text_field_theme.dart @@ -67,6 +67,7 @@ class ThemedFormField extends HookWidget { ), ); final inputDecoration = InputDecoration( + // isDense: true, errorStyle: TextStyle(color: const Color(0xFFD83731)), errorBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(10),