policy tracker fix
This commit is contained in:
parent
10e5751367
commit
7a6ec262b7
@ -527,7 +527,7 @@ class _DateFilterRowState extends ConsumerState<DateFilterRowPayout> {
|
||||
SizedBox(height: 5),
|
||||
SizedBox(
|
||||
height: 35,
|
||||
width: MediaQuery.of(context).size.width * 0.42,
|
||||
width: MediaQuery.of(context).size.width * 0.4,
|
||||
child: DropdownSearch<Map<String, dynamic>>.multiSelection(
|
||||
key: dropDownKeyPartner,
|
||||
|
||||
@ -567,7 +567,7 @@ class _DateFilterRowState extends ConsumerState<DateFilterRowPayout> {
|
||||
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<DateFilterRowPayout> {
|
||||
decoration: InputDecoration(
|
||||
filled: true,
|
||||
fillColor: Colors.white,
|
||||
hintText: "Search Partner...",
|
||||
hintText: "Search Referer...",
|
||||
hintStyle: GoogleFonts.inter(
|
||||
fontSize: 11,
|
||||
color: Colors.black,
|
||||
|
||||
@ -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<PayOutScreen> {
|
||||
dynamic totalCommission = '0';
|
||||
bool isEdit = false;
|
||||
|
||||
List<Map<String, dynamic>> masterPolicies = [];
|
||||
|
||||
final TextEditingController _searchStaffController = TextEditingController();
|
||||
|
||||
final GlobalKey<DropdownSearchState<Map<String, dynamic>>> dropDownKeyPOS =
|
||||
GlobalKey<DropdownSearchState<Map<String, dynamic>>>();
|
||||
|
||||
@ -136,6 +141,45 @@ class _PayOutScreenState extends ConsumerState<PayOutScreen> {
|
||||
}
|
||||
}
|
||||
|
||||
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<void> getPosList(int id) async {
|
||||
print('E104 => Fns called => $id');
|
||||
|
||||
@ -224,6 +268,7 @@ class _PayOutScreenState extends ConsumerState<PayOutScreen> {
|
||||
.map((e) => Map<String, dynamic>.from(e))
|
||||
.toList();
|
||||
|
||||
masterPolicies = List<Map<String, dynamic>>.from(response['data']);
|
||||
filteredPolicies = List<Map<String, dynamic>>.from(response['data']);
|
||||
|
||||
print("selectedPolicies → $filteredPolicies ");
|
||||
@ -678,7 +723,8 @@ class _PayOutScreenState extends ConsumerState<PayOutScreen> {
|
||||
),
|
||||
),
|
||||
// SizedBox(width: 10),
|
||||
Spacer(),
|
||||
// Spacer(),
|
||||
SizedBox(width: 10),
|
||||
Text(
|
||||
'Status: ${isPending ? 'Pending' : 'Completed'}',
|
||||
style: GoogleFonts.inter(
|
||||
@ -690,6 +736,22 @@ class _PayOutScreenState extends ConsumerState<PayOutScreen> {
|
||||
// : 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) ...[
|
||||
|
||||
92
lib/presentation/screens/staff/policy/policyPdf.dart
Normal file
92
lib/presentation/screens/staff/policy/policyPdf.dart
Normal file
@ -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<PolicyPdfViewer> createState() => _PolicyPdfViewerState();
|
||||
}
|
||||
|
||||
class _PolicyPdfViewerState extends State<PolicyPdfViewer>
|
||||
with AutomaticKeepAliveClientMixin {
|
||||
@override
|
||||
bool get wantKeepAlive => true; // Prevents rebuilding
|
||||
|
||||
// Cache the loaded PDF bytes
|
||||
Uint8List? _cachedPdfBytes;
|
||||
String? _lastLoadedUrl;
|
||||
|
||||
Future<Uint8List> _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<Uint8List>(
|
||||
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,
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -866,7 +866,7 @@ class policylistState extends ConsumerState<policylist> {
|
||||
? '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,
|
||||
|
||||
@ -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<policyValidation> {
|
||||
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<policyValidation> {
|
||||
final GlobalKey<DropdownSearchState<Map<String, dynamic>>> dropDownKeyFuel =
|
||||
GlobalKey<DropdownSearchState<Map<String, dynamic>>>();
|
||||
|
||||
bool _hasLoadedInitialData = false;
|
||||
|
||||
Map<String, dynamic> dataDetails() {
|
||||
return {
|
||||
"rc_no": controllers["rcNo"]?.text,
|
||||
@ -244,28 +249,50 @@ class _policyValidationState extends ConsumerState<policyValidation> {
|
||||
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<void> _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<policyValidation> {
|
||||
|
||||
setState(() {
|
||||
pdfUrl = "$url";
|
||||
_cachedPdfUrl = pdfUrl;
|
||||
});
|
||||
print("FINAL PDF URL -> $pdfUrl");
|
||||
} catch (e, st) {
|
||||
@ -416,7 +444,7 @@ class _policyValidationState extends ConsumerState<policyValidation> {
|
||||
// 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<policyValidation> {
|
||||
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<policyValidation> {
|
||||
}
|
||||
}
|
||||
|
||||
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<void> _fetchCommision() async {
|
||||
print('_fetchCommision IN');
|
||||
final policyId = widget.item?['policy_id'];
|
||||
@ -562,6 +603,17 @@ class _policyValidationState extends ConsumerState<policyValidation> {
|
||||
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<policyValidation> {
|
||||
|
||||
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<policyValidation> {
|
||||
|
||||
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<policyValidation> {
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
|
||||
Expanded(
|
||||
child: FutureBuilder<Uint8List>(
|
||||
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<Uint8List>(
|
||||
// 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<policyValidation> {
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
const SizedBox(width: 8),
|
||||
Expanded(
|
||||
child: _buildInput(
|
||||
'Premium Amount *',
|
||||
@ -1362,16 +1413,34 @@ class _policyValidationState extends ConsumerState<policyValidation> {
|
||||
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<policyValidation> {
|
||||
required: true,
|
||||
keyboardType:
|
||||
TextInputType.number,
|
||||
inputFormatters:
|
||||
digitsOnlyFormatter,
|
||||
inputFormatters: [
|
||||
FilteringTextInputFormatter
|
||||
.digitsOnly, // only numbers
|
||||
LengthLimitingTextInputFormatter(
|
||||
4,
|
||||
), // max 4 digits
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
|
||||
Loading…
Reference in New Issue
Block a user