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/Pagination.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/text_field_theme.dart'; import 'createQuotationPopUp.dart'; class QuotationScreen extends ConsumerStatefulWidget { const QuotationScreen({super.key}); @override ConsumerState createState() => QuotationScreenState(); } class QuotationScreenState 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? 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, "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) { setState(() { 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; }); showDialog( context: context, builder: (ctx) => createQuotatDialog( userId: userId, selectedQuotationFrmListdata: selectedQuotationFrmListData!, selectedQuotationFrmListId: selectedQuotationFrmListId!, managerId: managerId, selectedEnquiryId: selectedEnquiryId, onSubmit: (value) { debugPrint("New Endorsement: $value"); // enrollKey.currentState?.getEndrosmentList(selectedPolicyNumber); print(value); if (value == 'Success') { print('Success'); refresh(); } // Update claim list or call API }, ), ); } @override Widget build(BuildContext context) { final isMobile = ResponsiveLayout.isMobile(context); // final enqQuotation = ref.watch(quotationStaffIdProvider); // // if (enqQuotation != null && getQuotationData.isEmpty && !isLoading) { // // trigger load only once for this value // Future.microtask(() => _loadData(enqQuotation)); // } // ref.listen(quotationStaffIdProvider, (prev, next) { // if (prev != next && next != null) { // _loadData(next); // use the new value, not the old enqQuotation // } // }); return MainLayout( title: 'quotation', body: SelectionArea( child:Container( height: MediaQuery.of(context).size.height, width: MediaQuery.of(context).size.width, child: SingleChildScrollView( scrollDirection: Axis.vertical, child: Column( children: [ Container( height: 30, width: MediaQuery.of(context).size.width, child: GestureDetector( onTap: () { context.go(AppRoutes.enquiryForStaff); }, child: Row( crossAxisAlignment: CrossAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.start, children: [ Tooltip( message: 'Back', child: IconButton( icon: const Icon( Icons.arrow_left_sharp, size: 20, color: Color(0xFF425B5B), ), onPressed: () { context.go(AppRoutes.enquiryForStaff); }, splashRadius: 9, hoverColor: Colors.black12, padding: const EdgeInsets.all(4), constraints: const BoxConstraints(), ), ), const SizedBox(width: 5), // spacing between icon and text Text( "Quotation", style: GoogleFonts.inter( fontSize: 18, fontWeight: FontWeight.w600, ), ), ], ), ), ), SizedBox(height: 10), //1ST SECTION if (isMobile) ...[ 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), registerNumber(context), // SizedBox(height: 16), // insurer(context), SizedBox(height: 16), buildDocRC(context, isMobile), SizedBox(height: 16), buildDocIdProof(context, isMobile), SizedBox(height: 16), buildDocPrevPolicy(context, isMobile), ], ), ), ], if (!isMobile) ...[ 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: isMobile ? 14 : 16, fontWeight: FontWeight.w700, ), ), SizedBox(height: 15), Row( crossAxisAlignment: CrossAxisAlignment.end, children: [ registerNumber(context), SizedBox(width: 10), // insurer(context), // Spacer(), buildDocRC(context, isMobile), SizedBox(width: 10), buildDocIdProof(context, isMobile), SizedBox(width: 10), buildDocPrevPolicy(context, isMobile), ], ), ], ), ), ], const SizedBox(height: 20), if (!blockKey) ...[ Container( padding: EdgeInsets.only(right: 10), child: Row( mainAxisAlignment: MainAxisAlignment.end, children: [ GestureDetector( onTap: () { // print('Export'); setState(() { selectedQuotationFrmListId = null; selectedQuotationFrmListData = null; }); showDialog( context: context, builder: (ctx) => createQuotatDialog( userId: userId, selectedQuotationFrmListdata: selectedQuotationFrmListData, selectedQuotationFrmListId: selectedQuotationFrmListId, managerId: managerId, selectedEnquiryId: selectedEnquiryId, onSubmit: (value) { debugPrint("New Endorsement: $value"); // enrollKey.currentState?.getEndrosmentList(selectedPolicyNumber); print(value); if (value == 'Success') { print('Success'); refresh(); } // Update claim list or call API }, ), ); }, child: Container( padding: EdgeInsets.all(8.0), decoration: BoxDecoration( color: Color(0xFF425B5B), borderRadius: BorderRadius.circular(8.0), ), child: Row( mainAxisSize: MainAxisSize.min, children: [ Icon(Icons.add, color: Colors.white), SizedBox(width: 10), Text( 'Create New Quotation', style: GoogleFonts.inter( color: Colors.white, fontWeight: FontWeight.w500, fontSize: 13, ), ), ], ), ), ), ], ), ), const SizedBox(height: 20), ], //3RD SECTION (TABLE) 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(16.0), child: Column( children: [ // Text('datta') _buildDataTable(context), ], ), ), ], ), ), ),), ); } Widget _downloadButton(String label) { final isMobile = ResponsiveLayout.isMobile(context); return GestureDetector( onTap: () => apiService.downloadFile( apiUrl: 'quotation/downloadAdditionalUploadedFile?id=$selectedId', apiId: selectedId, localFile: docUploadedFile, fileName: selectedFileNames, ), child: Container( width: isMobile ? null : MediaQuery.of(context).size.width * 0.15, padding: const EdgeInsets.all(10), decoration: BoxDecoration( borderRadius: BorderRadius.circular(5), color: const Color(0xFF425B5B), ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( label, style: GoogleFonts.inter( fontSize: 12, fontWeight: FontWeight.w500, color: Colors.white, ), ), // const SizedBox(width: 5), const Icon(Icons.download, size: 13, color: Colors.white), ], ), ), ); } Widget buildDocRC(BuildContext context, bool isMobile) { return GestureDetector( onTap: () { // print("Upload $idget.id}{w"); final selectedId = selectedEnquiryId; // final path = // '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: [ Text( "Download RC", style: GoogleFonts.inter( fontSize: 12, fontWeight: FontWeight.w500, 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: GoogleFonts.inter( fontSize: 12, fontWeight: FontWeight.w500, 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("QD597 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: GoogleFonts.inter( fontSize: 12, fontWeight: FontWeight.w500, 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 registerNumber(BuildContext context) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text('Vehicle 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: "Vehicle 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) ? _buildDataRow(item, sno) : _buildDataCard(item, sno); }, ), ], ), ); } Widget _buildHeader() { return Container( decoration: BoxDecoration( color: Color(0xFFEDF6F5), borderRadius: BorderRadius.circular(6), ), padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 16), child: Row( children: [ // Expanded(flex: 3, child: Text(' ', style: _headerStyle)), Expanded(flex: 2, child: Text('Vehicle Number', style: _headerStyle)), Expanded(flex: 4, child: Text('Insurer', style: _headerStyle)), Expanded(flex: 2, child: Text('IDV', style: _headerStyle)), Expanded(flex: 2, child: Text('Plan Type', style: _headerStyle)), Expanded(flex: 2, child: Text('Premium Amount', style: _headerStyle)), Expanded(flex: 2, child: Text('Status', style: _headerStyle)), Expanded(flex: 1, child: Text('', style: _headerStyle)), ], ), ); } 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: 4, 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' // ? 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: 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: 14, 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.bold, ); 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, ); }