From 7a6ec262b735f3f0a1d9f98e564eaad87e2aecd9 Mon Sep 17 00:00:00 2001 From: venbaittech Date: Wed, 24 Dec 2025 17:46:51 +0530 Subject: [PATCH] policy tracker fix --- .../screens/payout/custom_dateRange.dart | 6 +- .../screens/payout/payout_screen.dart | 64 ++++- .../screens/staff/policy/policyPdf.dart | 92 ++++++ .../screens/staff/policy/policy_list.dart | 2 +- .../staff/policy/policy_validation.dart | 262 +++++++++++------- 5 files changed, 327 insertions(+), 99 deletions(-) create mode 100644 lib/presentation/screens/staff/policy/policyPdf.dart diff --git a/lib/presentation/screens/payout/custom_dateRange.dart b/lib/presentation/screens/payout/custom_dateRange.dart index d33b695..660991b 100644 --- a/lib/presentation/screens/payout/custom_dateRange.dart +++ b/lib/presentation/screens/payout/custom_dateRange.dart @@ -527,7 +527,7 @@ class _DateFilterRowState extends ConsumerState { SizedBox(height: 5), SizedBox( height: 35, - width: MediaQuery.of(context).size.width * 0.42, + width: MediaQuery.of(context).size.width * 0.4, child: DropdownSearch>.multiSelection( key: dropDownKeyPartner, @@ -567,7 +567,7 @@ class _DateFilterRowState extends ConsumerState { decoratorProps: DropDownDecoratorProps( decoration: AppInputDecorations.dropdownDecoration( - label: "Select Partner", + label: "Select Referer", ).copyWith( hintStyle: GoogleFonts.inter( fontSize: 12, @@ -608,7 +608,7 @@ class _DateFilterRowState extends ConsumerState { decoration: InputDecoration( filled: true, fillColor: Colors.white, - hintText: "Search Partner...", + hintText: "Search Referer...", hintStyle: GoogleFonts.inter( fontSize: 11, color: Colors.black, diff --git a/lib/presentation/screens/payout/payout_screen.dart b/lib/presentation/screens/payout/payout_screen.dart index ab25cc5..8cd8a5f 100644 --- a/lib/presentation/screens/payout/payout_screen.dart +++ b/lib/presentation/screens/payout/payout_screen.dart @@ -15,6 +15,7 @@ import '../../layouts/responsive_layout.dart'; import '../../providers/manager_provider.dart'; import '../../providers/userRoleProvider.dart'; import '../../themes/indicators/input_field_decoration.dart'; +import '../../themes/indicators/search_field_theme.dart'; import 'FormFieldBox.dart'; import 'custom_dateRange.dart'; @@ -44,6 +45,10 @@ class _PayOutScreenState extends ConsumerState { dynamic totalCommission = '0'; bool isEdit = false; + List> masterPolicies = []; + + final TextEditingController _searchStaffController = TextEditingController(); + final GlobalKey>> dropDownKeyPOS = GlobalKey>>(); @@ -136,6 +141,45 @@ class _PayOutScreenState extends ConsumerState { } } + void filterPolicyData(String query) { + final lowerQuery = query.toLowerCase(); + + print('filterPolicyData - $lowerQuery'); + + print('filteredPolicies1 - $filteredPolicies'); + setState(() { + if (query.trim().isEmpty) { + print('filteredPolicies2'); + filteredPolicies = List.from(masterPolicies); + return; + } + print('filteredPolicies3'); + filteredPolicies = masterPolicies.where((item) { + return (item['policy_no'] ?? '').toString().toLowerCase().contains( + lowerQuery, + ) || + (item['customer_name'] ?? '').toString().toLowerCase().contains( + lowerQuery, + ) || + (item['agent_name'] ?? '').toString().toLowerCase().contains( + lowerQuery, + ) || + (item['insurer_name'] ?? '').toString().toLowerCase().contains( + lowerQuery, + ) || + (item['premium_amount'] ?? '').toString().toLowerCase().contains( + lowerQuery, + ) || + (item['commission_amount'] ?? '').toString().toLowerCase().contains( + lowerQuery, + ) || + (_formatDate(item['issued_date']) ?? '').toLowerCase().contains( + lowerQuery, + ); + }).toList(); + }); + } + Future getPosList(int id) async { print('E104 => Fns called => $id'); @@ -224,6 +268,7 @@ class _PayOutScreenState extends ConsumerState { .map((e) => Map.from(e)) .toList(); + masterPolicies = List>.from(response['data']); filteredPolicies = List>.from(response['data']); print("selectedPolicies → $filteredPolicies "); @@ -678,7 +723,8 @@ class _PayOutScreenState extends ConsumerState { ), ), // SizedBox(width: 10), - Spacer(), + // Spacer(), + SizedBox(width: 10), Text( 'Status: ${isPending ? 'Pending' : 'Completed'}', style: GoogleFonts.inter( @@ -690,6 +736,22 @@ class _PayOutScreenState extends ConsumerState { // : const Color(0xFF047857), ), ), + + Spacer(), + + ThemedSearchField( + hintText: 'Search', + + // backgroundColor: Color(0xFFF6F8F8), + onChanged: filterPolicyData, + + controller: _searchStaffController, + backgroundColor: Color(0xFFFFFFFF), + txtHeight: 30, + txtwidth: ResponsiveLayout.isMobile(context) + ? MediaQuery.of(context).size.width * 0.7 + : MediaQuery.of(context).size.width * 0.15, + ), ], if (!isEdit) ...[ diff --git a/lib/presentation/screens/staff/policy/policyPdf.dart b/lib/presentation/screens/staff/policy/policyPdf.dart new file mode 100644 index 0000000..d234fbb --- /dev/null +++ b/lib/presentation/screens/staff/policy/policyPdf.dart @@ -0,0 +1,92 @@ +import 'package:dropdown_search/dropdown_search.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:go_router/go_router.dart'; +import 'package:google_fonts/google_fonts.dart'; +import 'package:http/http.dart' as http; +import 'package:intl/intl.dart'; +import 'package:pdf_render/pdf_render_widgets.dart'; +import 'dart:typed_data'; +import 'package:flutter/services.dart'; + +// ============= KEY FIX: Separate PDF Viewer Widget ============= +class PolicyPdfViewer extends StatefulWidget { + final String? pdfUrl; + + const PolicyPdfViewer({Key? key, this.pdfUrl}) : super(key: key); + + @override + State createState() => _PolicyPdfViewerState(); +} + +class _PolicyPdfViewerState extends State + with AutomaticKeepAliveClientMixin { + @override + bool get wantKeepAlive => true; // Prevents rebuilding + + // Cache the loaded PDF bytes + Uint8List? _cachedPdfBytes; + String? _lastLoadedUrl; + + Future _loadPdf() async { + // Return cached bytes if URL hasn't changed + if (_cachedPdfBytes != null && _lastLoadedUrl == widget.pdfUrl) { + return _cachedPdfBytes!; + } + + // Load new PDF + final response = await http.get(Uri.parse(widget.pdfUrl!)); + if (response.statusCode == 200) { + _cachedPdfBytes = response.bodyBytes; + _lastLoadedUrl = widget.pdfUrl; + return _cachedPdfBytes!; + } else { + throw Exception('Failed to load PDF: ${response.statusCode}'); + } + } + + @override + Widget build(BuildContext context) { + super.build(context); // Required for AutomaticKeepAliveClientMixin + + if (widget.pdfUrl == null) { + return const Center(child: CircularProgressIndicator()); + } + + return FutureBuilder( + key: ValueKey(widget.pdfUrl), // Only rebuild if URL changes + future: _loadPdf(), + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.waiting) { + return const Center(child: CircularProgressIndicator()); + } + + if (snapshot.hasError) { + return Center(child: Text('Error: ${snapshot.error}')); + } + + if (!snapshot.hasData) { + return const Center(child: CircularProgressIndicator()); + } + + return PdfDocumentLoader.openData( + snapshot.data!, + documentBuilder: (context, pdfDocument, pageCount) { + return ListView.builder( + itemCount: pageCount, + itemBuilder: (context, index) { + return Container( + color: Colors.black12, + child: PdfPageView( + pdfDocument: pdfDocument, + pageNumber: index + 1, + ), + ); + }, + ); + }, + ); + }, + ); + } +} diff --git a/lib/presentation/screens/staff/policy/policy_list.dart b/lib/presentation/screens/staff/policy/policy_list.dart index 9e8ec78..f2abef4 100644 --- a/lib/presentation/screens/staff/policy/policy_list.dart +++ b/lib/presentation/screens/staff/policy/policy_list.dart @@ -866,7 +866,7 @@ class policylistState extends ConsumerState { ? 'Verified' : 'To Verify', child: Container( - width: 80, // ⭐ FIXED WIDTH → Equal in both states + width: 30, // ⭐ FIXED WIDTH → Equal in both states padding: const EdgeInsets.symmetric( horizontal: 8, vertical: 4, diff --git a/lib/presentation/screens/staff/policy/policy_validation.dart b/lib/presentation/screens/staff/policy/policy_validation.dart index 38bdb0f..6c29c41 100644 --- a/lib/presentation/screens/staff/policy/policy_validation.dart +++ b/lib/presentation/screens/staff/policy/policy_validation.dart @@ -4,6 +4,8 @@ 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:intl/intl.dart'; +import 'package:nhance_partner/presentation/screens/staff/policy/policyPdf.dart'; import 'package:pdf_render/pdf_render_widgets.dart'; import 'package:syncfusion_flutter_pdfviewer/pdfviewer.dart'; @@ -41,6 +43,7 @@ class _policyValidationState extends ConsumerState { dynamic managerId; dynamic userId; + String? _cachedPdfUrl; String? pdfUrl; // full file path from API // --- Controllers (all editable fields) @@ -110,6 +113,8 @@ class _policyValidationState extends ConsumerState { final GlobalKey>> dropDownKeyFuel = GlobalKey>>(); + bool _hasLoadedInitialData = false; + Map dataDetails() { return { "rc_no": controllers["rcNo"]?.text, @@ -244,28 +249,50 @@ class _policyValidationState extends ConsumerState { for (final key in controllerKeys) key: TextEditingController(), }; update(); + _loadInitialData(); // read providers + call APIs - Future.microtask(() { - try { - managerId = ref.read(managerIdProvider); - } catch (e) { - managerId = null; - } - try { - userId = ref.read(userIdProvider); - } catch (e) { - userId = null; - } + // Future.microtask(() { + // try { + // managerId = ref.read(managerIdProvider); + // } catch (e) { + // managerId = null; + // } + // try { + // userId = ref.read(userIdProvider); + // } catch (e) { + // userId = null; + // } + // + // getVehicleType(); + // getFuelType(); + // getFindPolicy(); + // getPolicyFilePath(); + // }); - getFindPolicy(); - getVehicleType(); - getFuelType(); - getPolicyFilePath(); - }); + // controllers['commission_amount']?.addListener(() { + // setState(() {}); + // }); + } - controllers['commission_amount']?.addListener(() { - setState(() {}); - }); + Future _loadInitialData() async { + print('_loadInitialData'); + try { + managerId = ref.read(managerIdProvider); + } catch (e) { + managerId = null; + } + try { + userId = ref.read(userIdProvider); + } catch (e) { + userId = null; + } + + await Future.wait([ + getFindPolicy(), + getVehicleType(), + getFuelType(), + getPolicyFilePath(), + ]); } @override @@ -361,6 +388,7 @@ class _policyValidationState extends ConsumerState { setState(() { pdfUrl = "$url"; + _cachedPdfUrl = pdfUrl; }); print("FINAL PDF URL -> $pdfUrl"); } catch (e, st) { @@ -416,7 +444,7 @@ class _policyValidationState extends ConsumerState { // controllers["startDate"]!.text = safeText(data['start_date']); // controllers["endDate"]!.text = safeText(data['end_date']); - controllers["issueDate"]?.text = fixInvalidDate(data['issued_date']); + controllers["issuedDate"]?.text = fixInvalidDate(data['issued_date']); controllers["startDate"]?.text = fixInvalidDate(data['start_date']); controllers["endDate"]?.text = fixInvalidDate(data['end_date']); @@ -451,6 +479,10 @@ class _policyValidationState extends ConsumerState { selectedInsuranceId = safeText(data['insurer_id']); selectedAgentRentionRate = safeText(data['agent_retention_rate']); selectedManagerRentionRate = safeText(data['manager_retention_rate']); + + controllers["commission_amount"]?.text = safeText( + data['commission_amount'], + ); }); debugPrint('findPolicyApi response3:'); @@ -553,6 +585,15 @@ class _policyValidationState extends ConsumerState { } } + DateTime? parseDate(String? value) { + if (value == null || value.isEmpty) return null; + try { + return DateFormat('dd-MM-yyyy').parse(value); // change format if required + } catch (_) { + return null; + } + } + Future _fetchCommision() async { print('_fetchCommision IN'); final policyId = widget.item?['policy_id']; @@ -562,6 +603,17 @@ class _policyValidationState extends ConsumerState { return; } + final startDate = parseDate(controllers["startDate"]?.text); + final endDate = parseDate(controllers["endDate"]?.text); + + if (startDate != null && endDate != null && startDate.isAfter(endDate)) { + ToastHelper.showWarningToast( + context, + 'Start Date cannot be greater than End Date', + ); + return; + } + if (!_formKey.currentState!.validate()) { ToastHelper.showWarningToast(context, "Please fill all required fields"); return; @@ -822,11 +874,11 @@ class _policyValidationState extends ConsumerState { onChanged: (val) { if (val != null) { - setState(() { - selectedVehicleType = val; - controllers['vehicleType']?.text = val; - }); - + // setState(() { + selectedVehicleType = val; + controllers['vehicleType']?.text = val; + // }); + setState(() {}); print("Selected Vehicle Type: $val"); } }, @@ -927,11 +979,11 @@ class _policyValidationState extends ConsumerState { onChanged: (val) { if (val != null) { - setState(() { - selectedFuelType = val; - controllers['fuelType']?.text = val; - }); - + // setState(() { + selectedFuelType = val; + controllers['fuelType']?.text = val; + // }); + setState(() {}); print("Selected Fuel Type: $val"); } }, @@ -1187,60 +1239,62 @@ class _policyValidationState extends ConsumerState { ), ), const SizedBox(height: 8), - Expanded( - child: FutureBuilder( - future: loadNetworkPdfBytes(pdfUrl!), - builder: (context, snapshot) { - if (!snapshot.hasData) { - return const Center( - child: CircularProgressIndicator(), - ); - } - - final pdfBytes = snapshot.data!; - - return PdfDocumentLoader.openData( - pdfBytes, - documentBuilder: - (context, pdfDocument, pageCount) { - return ListView.builder( - itemCount: pageCount, - itemBuilder: (context, index) { - return Container( - color: Colors.black12, - child: PdfPageView( - pdfDocument: pdfDocument, - pageNumber: index + 1, - ), - ); - }, - ); - }, - ); - }, - ), + child: PolicyPdfViewer( + pdfUrl: pdfUrl, + ), // 👈 Use separate widget ), // Expanded( - // child: PdfDocumentLoader.openAsset( - // 'assets/pdfs/sample.pdf', - // documentBuilder: (context, pdfDocument, pageCount) => LayoutBuilder( - // builder: (context, constraints) => ListView.builder( - // itemCount: pageCount, - // itemBuilder: (context, index) => Container( - // // margin: EdgeInsets.all(margin), - // // padding: EdgeInsets.all(padding), - // color: Colors.black12, - // child: PdfPageView( - // pdfDocument: pdfDocument, - // pageNumber: index + 1, - // ) - // ) - // ) - // ), - // ) - // ) + // child: pdfUrl == null + // ? Center(child: CircularProgressIndicator()) + // : FutureBuilder( + // key: ValueKey( + // _cachedPdfUrl, + // ), // 👈 Add this key + // future: loadNetworkPdfBytes( + // _cachedPdfUrl!, + // ), + // // future: loadNetworkPdfBytes(pdfUrl!), + // builder: (context, snapshot) { + // if (!snapshot.hasData) { + // return const Center( + // child: + // CircularProgressIndicator(), + // ); + // } + // + // final pdfBytes = snapshot.data!; + // + // return PdfDocumentLoader.openData( + // pdfBytes, + // documentBuilder: + // ( + // context, + // pdfDocument, + // pageCount, + // ) { + // return ListView.builder( + // itemCount: pageCount, + // itemBuilder: + // (context, index) { + // return Container( + // color: + // Colors.black12, + // child: PdfPageView( + // pdfDocument: + // pdfDocument, + // pageNumber: + // index + 1, + // ), + // ); + // }, + // ); + // }, + // ); + // }, + // ), + // ), ], ), ), @@ -1344,10 +1398,7 @@ class _policyValidationState extends ConsumerState { ), ), ), - ], - ), - Row( - children: [ + const SizedBox(width: 8), Expanded( child: _buildInput( 'Premium Amount *', @@ -1362,16 +1413,34 @@ class _policyValidationState extends ConsumerState { decimalFormatter, ), ), - const SizedBox(width: 8), - Expanded( - child: _buildInput( - 'Remarks *', - required: false, - controllers['remarks']!, - ), - ), ], ), + // Row( + // children: [ + // Expanded( + // child: _buildInput( + // 'Premium Amount *', + // controllers['premiumAmount']!, + // + // required: true, + // keyboardType: + // TextInputType.numberWithOptions( + // decimal: true, + // ), + // inputFormatters: + // decimalFormatter, + // ), + // ), + // const SizedBox(width: 8), + // // Expanded( + // // child: _buildInput( + // // 'Remarks *', + // // required: false, + // // controllers['remarks']!, + // // ), + // // ), + // ], + // ), ], ), ), @@ -1579,8 +1648,13 @@ class _policyValidationState extends ConsumerState { required: true, keyboardType: TextInputType.number, - inputFormatters: - digitsOnlyFormatter, + inputFormatters: [ + FilteringTextInputFormatter + .digitsOnly, // only numbers + LengthLimitingTextInputFormatter( + 4, + ), // max 4 digits + ], ), ), ],