// 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 '../../../../core/config/env.dart'; import '../../../../core/routing/routes.dart'; import '../../../../core/services/api_service.dart'; import '../../../../data/services/auth_service.dart'; import 'package:toastification/toastification.dart'; import 'package:universal_html/html.dart' as html; import '../../../../core/routing/routes.dart'; import '../../../../core/services/api_service.dart'; import '../../../../data/services/auth_service.dart'; import '../../../../data/utils/Pagination.dart'; import '../../../../data/utils/toastNotification.dart'; import '../../../../data/utils/validators.dart'; import '../../../layouts/main_layout.dart'; import '../../../layouts/responsive_layout.dart'; import '../../../providers/manager_provider.dart'; import '../../../providers/quotation_staff_proivder.dart'; import '../../../themes/indicators/customizd_file_upload.dart'; import '../../../themes/indicators/date_field_theme.dart'; import '../../../themes/indicators/text_field_theme.dart'; import 'package:file_picker/file_picker.dart'; class PolicyScreen extends ConsumerStatefulWidget { const PolicyScreen({super.key}); @override ConsumerState createState() => PolicyScreenState(); } class PolicyScreenState 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']; }); } 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'] = Env.App_Signature; // If updating, spoof the method Laravel-style if (isUpdating) { request.fields['id'] = id!; request.fields['updated_by'] = userId!.toString(); } else { request.fields['created_by'] = userId!.toString(); } print("USerDAta - $userData"); // userData.forEach((key, value) { // request.fields[key] = value.toString(); // print("✅ Encoded travel_details2: ${request.fields[key]}"); // }); await attachFiles(request); userData.forEach((key, value) { if (key != 'policy_pdf_file_name' && key != 'policy_payment_receipt_file_name') { request.fields[key] = value.toString(); print("✅ Encoded $key: ${request.fields[key]}"); } else { print('Something Missing..'); } }); // attach files // request.fields['agent_id'] = selectedId.toString(); print(" Sending request with fields: ${request.fields}"); try { final streamedResponse = await request.send(); final response = await http.Response.fromStream(streamedResponse); print("Response status: ${response.statusCode}"); print("Response body: ${response.body}"); if (response.statusCode == 200 || response.statusCode == 201) { // dispose(); print("✅ Agent submitted successfully!"); ToastHelper.showSuccessToast(context, 'Saved Policy'); print("Response: ${response.body}"); if (isUpdating) { context.go(AppRoutes.policylist); } else { ref.read(enquiryIdProvider.notifier).state = null; context.go(AppRoutes.policylist); } } else { print("❌ Submission failed. Status: ${response.statusCode}"); print("Body: ${response.body}"); showDialog( context: context, builder: (BuildContext context) { return AlertDialog( title: Text("Policy Creation Failed"), content: Text( "There was a problem in creating policy. Please try again.", ), actions: [ TextButton( child: Text("OK"), onPressed: () { Navigator.of(context).pop(); }, ), ], ); }, ); } } catch (e) { print("🔥 Error submitting user: $e"); } } @override Widget build(BuildContext context) { return MainLayout( title: 'policy', body: SelectionArea( child:Container( // height: MediaQuery.of(context).size.height, // width: MediaQuery.of(context).size.width, // padding: const EdgeInsets.all(20), child: SingleChildScrollView( scrollDirection: Axis.vertical, child: Column( children: [ Container( height: 40, width: MediaQuery.of(context).size.width, child: GestureDetector( onTap: () { context.go(AppRoutes.enquiryForStaff); }, child: Row( crossAxisAlignment: CrossAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.start, children: [ Tooltip( message: 'Back', child: IconButton( icon: const Icon( Icons.arrow_left_sharp, size: 25, color: Color(0xFF425B5B), ), onPressed: () { context.go(AppRoutes.enquiryForStaff); }, splashRadius: 18, hoverColor: Colors.black12, padding: const EdgeInsets.all(4), constraints: const BoxConstraints(), ), ), const SizedBox(width: 5), // spacing between icon and text Text( "Policy", style: GoogleFonts.inter( fontSize: 18, fontWeight: FontWeight.w600, ), ), ], ), ), ), SizedBox(height: 10), //1ST SECTION Container( decoration: BoxDecoration( color: const Color(0xFFEDF6F5), borderRadius: BorderRadius.circular(10), ), padding: const EdgeInsets.all(20), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Enquiry Details", style: GoogleFonts.inter( fontSize: 16, fontWeight: FontWeight.w700, ), ), SizedBox(height: 15), LayoutBuilder( builder: (context, constraints) { final isMobile = constraints.maxWidth < 600; // breakpoint if (isMobile) { // 📱 MOBILE → Column layout return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ registernumber(context), const SizedBox(height: 16), insurer(context), const SizedBox(height: 16), buildIdv(context), const SizedBox(height: 16), buildPremiumAmnt(context), const SizedBox(height: 16), buildPlanType(context), const SizedBox(height: 16), buildDocRC(context, isMobile), SizedBox(height: 16), buildDocIdProof(context, isMobile), SizedBox(height: 16), buildDocPrevPolicy(context, isMobile), SizedBox(height: 16), buildDocQuotation(context, isMobile), ], ); } else { // 💻 WEB → Row layout return Column( children: [ Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Expanded( flex: 2, child: registernumber(context), ), const SizedBox(width: 20), Expanded(flex: 2, child: insurer(context)), const SizedBox(width: 20), Expanded(flex: 2, child: buildIdv(context)), const SizedBox(width: 20), Expanded( flex: 2, child: buildPremiumAmnt(context), ), const SizedBox(width: 20), Expanded( flex: 2, child: buildPlanType(context), ), const SizedBox(width: 20), ], ), const SizedBox(height: 20), Row( mainAxisAlignment: MainAxisAlignment.start, children: [ buildDocRC(context, isMobile), SizedBox(width: 12), buildDocIdProof(context, isMobile), SizedBox(width: 12), buildDocPrevPolicy(context, isMobile), SizedBox(width: 12), buildDocQuotation(context, isMobile), ], ), ], ); } }, ), ], ), ), const SizedBox(height: 20), //2ND SECTION Container( // color: Colors.green, decoration: BoxDecoration( color: Color(0xffEDF6F5), borderRadius: BorderRadius.circular(10.0), ), width: MediaQuery.of(context).size.width, // height: MediaQuery.of(context).size.height * 0.8, padding: EdgeInsets.all(26.0), child: Column( children: [ SingleChildScrollView(child: buildFormFields(context)), const SizedBox(height: 20), //confirm button // if (!hasPolicyData) Row( mainAxisAlignment: MainAxisAlignment.end, children: [ GestureDetector( onTap: () { handleSave(); }, child: Container( padding: EdgeInsets.symmetric( horizontal: 45.0, vertical: 8, ), decoration: BoxDecoration( borderRadius: BorderRadius.circular(8.0), color: Color(0xFF425B5B), ), child: Text( 'Submit', style: TextStyle(color: Colors.white), ), ), ), ], ), ], ), ), const SizedBox(height: 20), ], ), ), )), ); } Widget buildDocRC(BuildContext context, bool isMobile) { return GestureDetector( onTap: () { // print("Upload $idget.id}{w"); final selectedId = selectedEnquiryId; // final path = // 'agent/downloadAgentIncentiveFile?id=$selectedId'; final path = '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 = // 'agent/downloadAgentIncentiveFile?id=$selectedId'; final path = '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 = // 'agent/downloadAgentIncentiveFile?id=$selectedId'; final path = '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 = // 'agent/downloadAgentIncentiveFile?id=$selectedId'; // final path = // 'enquiry/downloadEnquiryFile?enquiry_id=$selectedId&file_type=previous_policy'; final path = '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, Insuredname(context), premiumamount(context), policynumber(context), ), if (!ResponsiveLayout.isMobile(context)) const SizedBox(height: 10), _buildResponsiveRow( context, Paymentmode(context), policyissuedate(context), policystartdate(context), ), if (!ResponsiveLayout.isMobile(context)) const SizedBox(height: 10), _buildResponsiveRow( context, policyenddate(context), buildUploadPolicyPdf(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('Vehicle 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('Policy Issue Date *', style: _textStyle), SizedBox(height: 10), ThemedDateField( hintText: "Select Date", txtwidth: ResponsiveLayout.isMobile(context) ? null : MediaQuery.of(context).size.width * 0.26, txtheight: 50, // backgroundColor: const Color(0xFFECECEC), validator: (value) => Validators.requiredField(value, "date"), controller: controllers['issueDate']!, onDateSelected: (date) { print("Picked Date: $date"); controllers['issueDate']?.text = DateFormat( 'dd-MM-yyyy', ).format(date); // controllers['date']?.text = date as String; }, ), ], ); } Widget policystartdate(BuildContext context) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text('Policy Start Date *', style: _textStyle), SizedBox(height: 10), ThemedDateField( hintText: "Select Date", txtwidth: ResponsiveLayout.isMobile(context) ? null : MediaQuery.of(context).size.width * 0.26, txtheight: 50, // backgroundColor: const Color(0xFFECECEC), validator: (value) => Validators.requiredField(value, "date"), controller: controllers['startDate']!, onDateSelected: (date) { print("Picked Date: $date"); controllers['startDate']?.text = DateFormat( 'dd-MM-yyyy', ).format(date); // auto set end date (1 year later - 1 day if needed) // final endDate = DateTime( // date.year + 1, // date.month, // date.day, // ); // optional: subtract 1 day final endDate = DateTime( date.year + 1, date.month, date.day, ).subtract(const Duration(days: 1)); 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('Policy End Date *', style: _textStyle), SizedBox(height: 10), ThemedDateField( hintText: "Select Date", txtwidth: ResponsiveLayout.isMobile(context) ? null : MediaQuery.of(context).size.width * 0.26, txtheight: 50, // backgroundColor: const Color(0xFFECECEC), // validator: (value) => Validators.requiredField(value, "date"), // 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 startText = controllers['startDate']?.text; if (startText == null || startText.isEmpty) { return "Select start date first"; } 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"; } // Add 365 days - 1 day to start date final expectedEndDate = startDate .add(const Duration(days: 365)) .subtract(const Duration(days: 1)); 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; }, 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), Column( mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [ ThemedUploadField( hintText: selectedPDFFileNames ?? "Upload Document", txtwidth: ResponsiveLayout.isMobile(context) ? null : MediaQuery.of(context).size.width * 0.26, // backgroundColor: Color(0xFFEDF6F5), onFileSelected: (fileName, file) { print("Picked file: $fileName (${file.size} bytes)"); setState(() { docPDFUploadedFile = file; }); }, ), const SizedBox(height: 5), if (docUploadedPDFFileUrlFromApi != null) Container( // color: Colors.white, child: Row( mainAxisSize: MainAxisSize.min, mainAxisAlignment: MainAxisAlignment.end, children: [ GestureDetector( onTap: () => apiService.downloadFile( apiUrl: 'policy/downloadPolicyFile?policy_id=$choosedPolcyId&file_type=policy_pdf', apiId: choosedPolcyId, localFile: docPDFUploadedFile, fileName: selectedPDFFileNames, ), child: Container( padding: const EdgeInsets.all(5), decoration: BoxDecoration( borderRadius: BorderRadius.circular(5), color: Color(0xFF425B5B), // color: Colors.green.shade300, ), child: Row( children: const [ Text( "Download", style: TextStyle( fontSize: 12, fontWeight: FontWeight.w200, color: Colors.white, ), ), SizedBox(width: 5), Icon(Icons.download, size: 13, color: Colors.white), ], ), ), ), ], ), ), ], ), ], ); } Widget buildUploadPolicyReceipt(BuildContext context) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text('Upload Policy Receipt *', style: _textStyle), const SizedBox(height: 10), Column( mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [ ThemedUploadField( hintText: selectedFileNames ?? "Upload Document", txtwidth: ResponsiveLayout.isMobile(context) ? null : MediaQuery.of(context).size.width * 0.26, // backgroundColor: Color(0xFFEDF6F5), onFileSelected: (fileName, file) { print("Picked file: $fileName (${file.size} bytes)"); setState(() { docUploadedFile = file; }); }, ), const SizedBox(height: 5), if (docUploadedFileUrlFromApi != null) Container( // color: Colors.white, child: Row( mainAxisSize: MainAxisSize.min, mainAxisAlignment: MainAxisAlignment.end, children: [ GestureDetector( onTap: () => apiService.downloadFile( apiUrl: '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: 14, fontWeight: FontWeight.w600, ); }