This commit is contained in:
venbaittech 2025-12-15 16:20:42 +05:30
commit b9f2032d3b
3 changed files with 436 additions and 423 deletions

File diff suppressed because one or more lines are too long

View File

@ -55,6 +55,15 @@ import 'dart:typed_data';
// ); // );
// } // }
// } // }
import 'package:flutter/services.dart';
final digitsOnlyFormatter = [
FilteringTextInputFormatter.digitsOnly,
];
final decimalFormatter = [
FilteringTextInputFormatter.allow(RegExp(r'^\d*\.?\d{0,2}$')),
];
class policyValidation extends ConsumerStatefulWidget { class policyValidation extends ConsumerStatefulWidget {
final dynamic item; final dynamic item;
@ -109,6 +118,8 @@ class _policyValidationState extends ConsumerState<policyValidation> {
late TextEditingController commissionAmountController; late TextEditingController commissionAmountController;
final GlobalKey<SfPdfViewerState> _pdfViewerKey = GlobalKey(); final GlobalKey<SfPdfViewerState> _pdfViewerKey = GlobalKey();
final _formKey = GlobalKey<FormState>();
@override @override
void initState() { void initState() {
@ -247,14 +258,6 @@ class _policyValidationState extends ConsumerState<policyValidation> {
Future<void> getPolicyFilePath() async { Future<void> getPolicyFilePath() async {
_pdfViewerKey.currentState?.openBookmarkView(); _pdfViewerKey.currentState?.openBookmarkView();
// 🔥 Use DIRECT FIXED URL you asked for
// const directPdfUrl = "https://venbait.in/nhance/mypdf.pdf";
//
// setState(() {
// pdfUrl = directPdfUrl;
// });
//
// debugPrint("FINAL PDF URL -> $pdfUrl");
final policyId = widget.item?['policy_id']; final policyId = widget.item?['policy_id'];
if (policyId == null) { if (policyId == null) {
@ -273,42 +276,10 @@ class _policyValidationState extends ConsumerState<policyValidation> {
setState(() { setState(() {
pdfUrl = "$url"; pdfUrl = "$url";
}); });
print("FINAL PDF URL -> $pdfUrl");
final response = await apiService.policyFilePathApi(
policyId,
'policy_pdf',
);
debugPrint('policyFilePathApi response: $response');
if (response != null && response['status'] == 'success') {
// final filePath = response['data']?.toString();
//
// // The API returns a server absolute path like:
// // /home3/.../uploads/policy/policy_pdf/yourfile.pdf
// // You must convert it to a usable URL for web/mobile.
//
// if (filePath != null && filePath.isNotEmpty) {
// // EXAMPLE:
// // Build full URL based on your domain
// // final baseUrl = filePath;
// final baseUrl = "https://venbait.in/nhance/mypdf.pdf";
//
// final fileName = filePath.split('/').last;
//
// setState(() {
// pdfUrl = "$baseUrl$fileName";
// });
//
// debugPrint("FINAL PDF URL -> $pdfUrl");
// }
} else {
debugPrint(
'policyFilePathApi returned error: ${response?['message'] ?? 'unknown'}',
);
}
} catch (e, st) { } catch (e, st) {
debugPrint('Exception in getPolicyFilePath: $e\n$st'); print('Exception in getPolicyFilePath: $e\n$st');
} finally { } finally {
if (!mounted) return; if (!mounted) return;
setState(() => isLoading = false); setState(() => isLoading = false);
@ -437,11 +408,24 @@ class _policyValidationState extends ConsumerState<policyValidation> {
Future<void> _save() async { Future<void> _save() async {
final policyId = widget.item?['policy_id']; final policyId = widget.item?['policy_id'];
if (policyId == null) { if (policyId == null) {
ToastHelper.showWarningToast(context, 'Missing policy id'); ToastHelper.showWarningToast(context, 'Missing policy id');
return; return;
} }
if (!_formKey.currentState!.validate()) {
ToastHelper.showWarningToast(context, "Please fill all required fields");
return;
}
if (widget.item?['is_data_accuracy_checked'] == "1") {
ToastHelper.showWarningToast(context, "Data already confirmed");
return;
}
final payload = { final payload = {
"id": policyId, "id": policyId,
"rc_no": rcNoController.text, "rc_no": rcNoController.text,
@ -560,33 +544,58 @@ class _policyValidationState extends ConsumerState<policyValidation> {
} }
Widget _buildInput( Widget _buildInput(
String label, String label,
TextEditingController controller, { TextEditingController controller, {
TextInputType keyboardType = TextInputType.text, TextInputType keyboardType = TextInputType.text,
bool readOnly = false, bool readOnly = false,
VoidCallback? onTap, VoidCallback? onTap,
}) { bool required = false,
List<TextInputFormatter>? inputFormatters, // 👈 ADD THIS
}) {
return Column( return Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text(label, style: const TextStyle(fontWeight: FontWeight.w500)), Text(label, style: const TextStyle(fontWeight: FontWeight.w500)),
const SizedBox(height: 6), const SizedBox(height: 6),
TextField( TextFormField(
controller: controller, controller: controller,
keyboardType: keyboardType, keyboardType: keyboardType,
readOnly: readOnly, readOnly: readOnly,
onTap: onTap, onTap: onTap,
inputFormatters: inputFormatters, // 👈 USE HERE
validator: (value) {
if (required && (value == null || value.trim().isEmpty)) {
return "$label is required";
}
return null;
},
decoration: InputDecoration( decoration: InputDecoration(
border: OutlineInputBorder(borderRadius: BorderRadius.circular(6)), border: OutlineInputBorder(borderRadius: BorderRadius.circular(6)),
isDense: true, isDense: true,
// alignment fix
helperText: ' ',
helperStyle: const TextStyle(height: 1),
errorMaxLines: 2,
errorBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(6),
borderSide: const BorderSide(color: Colors.red),
),
focusedErrorBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(6),
borderSide: const BorderSide(color: Colors.red),
),
contentPadding: const EdgeInsets.all(10), contentPadding: const EdgeInsets.all(10),
), ),
), ),
const SizedBox(height: 12),
], ],
); );
} }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
// Ensure global AppHeader remains visible by pushing content down by 64px // Ensure global AppHeader remains visible by pushing content down by 64px
@ -755,388 +764,392 @@ class _policyValidationState extends ConsumerState<policyValidation> {
Expanded( Expanded(
flex: 1, flex: 1,
child: SingleChildScrollView( child: SingleChildScrollView(
child: Card( child: Form(
elevation: 2, key: _formKey,
color: Colors.white, autovalidateMode: AutovalidateMode.onUserInteraction,
margin: EdgeInsets.zero, // flush with container child: Card(
shape: RoundedRectangleBorder( elevation: 2,
borderRadius: BorderRadius.circular(8), color: Colors.white,
), margin: EdgeInsets.zero,
child: Padding( shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
padding: const EdgeInsets.all(16), child: Padding(
child: Column( padding: const EdgeInsets.all(16),
crossAxisAlignment: CrossAxisAlignment.stretch, child: Column(
children: [ crossAxisAlignment: CrossAxisAlignment.stretch,
// Policy Details children: [
Card( // Policy Details
elevation: 5, Card(
color: Colors.white, elevation: 5,
shape: RoundedRectangleBorder( color: Colors.white,
borderRadius: BorderRadius.circular(8), shape: RoundedRectangleBorder(
), borderRadius: BorderRadius.circular(8),
child: Padding(
padding: const EdgeInsets.all(12.0),
child: Column(
crossAxisAlignment:
CrossAxisAlignment.stretch,
children: [
_sectionHeader(
'Policy Details',
icon: Icons.policy,
),
SizedBox(height: 10),
Row(
children: [
Expanded(
child: _buildInput(
'Policy Number',
policyNumberController,
),
),
const SizedBox(width: 8),
Expanded(
child: _buildInput(
'Insured Name',
insuredNameController,
),
),
],
),
Row(
children: [
Expanded(
child: _buildInput(
'Issued Date',
issuedDateController,
readOnly: true,
onTap: () => _pickDate(
issuedDateController,
context,
),
),
),
const SizedBox(width: 8),
Expanded(
child: _buildInput(
'Start Date',
startDateController,
readOnly: true,
onTap: () => _pickDate(
startDateController,
context,
),
),
),
const SizedBox(width: 8),
Expanded(
child: _buildInput(
'End Date',
endDateController,
readOnly: true,
onTap: () => _pickDate(
endDateController,
context,
),
),
),
],
),
Row(
children: [
Expanded(
child: _buildInput(
'Premium Amount',
premiumController,
keyboardType:
TextInputType.number,
),
),
const SizedBox(width: 8),
Expanded(
child: _buildInput(
'Remarks',
remarksController,
),
),
],
),
],
), ),
), child: Padding(
), padding: const EdgeInsets.all(12.0),
child: Column(
const SizedBox(height: 12), crossAxisAlignment:
CrossAxisAlignment.stretch,
// Tax Details children: [
Card( _sectionHeader(
elevation: 5, 'Policy Details',
color: Colors.white, icon: Icons.policy,
shape: RoundedRectangleBorder( ),
borderRadius: BorderRadius.circular(8), SizedBox(height: 10),
), Row(
child: Padding( children: [
padding: const EdgeInsets.all(12.0), Expanded(
child: Column(
crossAxisAlignment:
CrossAxisAlignment.stretch,
children: [
_sectionHeader(
'Tax / Cover Details',
icon: Icons.attach_money,
),
SizedBox(height: 10),
Row(
children: [
Expanded(
child: _buildInput(
'TP',
tpController,
keyboardType:
TextInputType.number,
),
),
const SizedBox(width: 8),
Expanded(
child: _buildInput(
'OD',
odController,
keyboardType:
TextInputType.number,
),
),
const SizedBox(width: 8),
Expanded(
child: _buildInput(
'PA',
paController,
keyboardType:
TextInputType.number,
),
),
],
),
Row(
children: [
Expanded(
child: _buildInput(
'CGST',
cgstController,
keyboardType:
TextInputType.number,
),
),
const SizedBox(width: 8),
Expanded(
child: _buildInput(
'SGST',
sgstController,
keyboardType:
TextInputType.number,
),
),
const SizedBox(width: 8),
Expanded(
child: _buildInput(
'IGST',
igstController,
keyboardType:
TextInputType.number,
),
),
],
),
],
),
),
),
const SizedBox(height: 12),
// Vehicle Details
Card(
elevation: 5,
color: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
child: Padding(
padding: const EdgeInsets.all(12.0),
child: Column(
crossAxisAlignment:
CrossAxisAlignment.stretch,
children: [
_sectionHeader(
'Vehicle Details',
icon: Icons.directions_car,
),
SizedBox(height: 10),
Row(
children: [
Expanded(
child: _buildInput(
'RTO State Name',
rtoStateCodeController,
),
),
const SizedBox(width: 8),
Expanded(
child: _buildInput(
'RTO City Code',
rtoCityCodeController,
),
),
const SizedBox(width: 8),
Expanded(
child: _buildInput(
'Weight',
weightController,
keyboardType:
TextInputType.number,
),
),
],
),
Row(
children: [
Expanded(
child: _buildInput(
'Fuel Type',
fuelTypeController,
),
),
const SizedBox(width: 8),
Expanded(
child: _buildInput(
'Date of Registration',
registrationDateController,
readOnly: true,
onTap: () => _pickDate(
registrationDateController,
context,
),
),
),
const SizedBox(width: 8),
Expanded(
child: _buildInput(
'Year of Manufacture',
yomController,
keyboardType:
TextInputType.number,
),
),
],
),
Row(
children: [
Expanded(
child: _buildInput(
'Engine No',
engineNoController,
),
),
const SizedBox(width: 8),
Expanded(
child: _buildInput(
'Chassis No',
chassisNoController,
),
),
const SizedBox(width: 8),
Expanded(
child: Expanded(
child: _buildInput( child: _buildInput(
'Make', 'Policy Number',
makeController, policyNumberController, required: true,
), ),
), ),
), const SizedBox(width: 8),
], Expanded(
), child: _buildInput(
Row( 'Insured Name',
children: [ insuredNameController, required: true
Expanded( ),
child: _buildInput(
'Model',
modelController,
), ),
), ],
const SizedBox(width: 8), ),
Expanded( Row(
child: _buildInput( children: [
'Cubic Capacity', Expanded(
cubicCapacityController, child: _buildInput(
keyboardType: 'Issued Date',
TextInputType.number, issuedDateController,
readOnly: true, required: true,
onTap: () => _pickDate(
issuedDateController,
context,
),
),
), ),
), const SizedBox(width: 8),
const SizedBox(width: 8), Expanded(
Expanded( child: _buildInput(
child: _buildInput( 'Start Date',
'Vehicle Type', startDateController,
vehicleTypeController, readOnly: true, required: true,
onTap: () => _pickDate(
startDateController,
context,
),
),
), ),
), const SizedBox(width: 8),
], Expanded(
), child: _buildInput(
], 'End Date',
endDateController,
readOnly: true, required: true,
onTap: () => _pickDate(
endDateController,
context,
),
),
),
],
),
Row(
children: [
Expanded(
child: _buildInput(
'Premium Amount',
premiumController,required: true,
keyboardType: TextInputType.numberWithOptions(decimal: true),
inputFormatters: decimalFormatter,
),
),
const SizedBox(width: 8),
Expanded(
child: _buildInput(
'Remarks',
remarksController,required: false
),
),
],
),
],
),
), ),
), ),
),
const SizedBox(height: 12), const SizedBox(height: 12),
// Broker Details // Tax Details
Card( Card(
elevation: 5, elevation: 5,
color: Colors.white, color: Colors.white,
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8), borderRadius: BorderRadius.circular(8),
), ),
child: Padding( child: Padding(
padding: const EdgeInsets.all(12.0), padding: const EdgeInsets.all(12.0),
child: Column( child: Column(
crossAxisAlignment: crossAxisAlignment:
CrossAxisAlignment.stretch, CrossAxisAlignment.stretch,
children: [ children: [
_sectionHeader( _sectionHeader(
'Broker / Commission', 'Tax / Cover Details',
icon: Icons.person, icon: Icons.attach_money,
), ),
SizedBox(height: 10), SizedBox(height: 10),
Row( Row(
children: [ children: [
Expanded( Expanded(
child: _buildInput( child: _buildInput(
'Broker Name', 'TP',
brokerNameController, tpController,required: true,
keyboardType: TextInputType.numberWithOptions(decimal: true),
inputFormatters: decimalFormatter,
),
), ),
), const SizedBox(width: 8),
const SizedBox(width: 8), Expanded(
Expanded( child: _buildInput(
child: _buildInput( 'OD',
'Commission Amount', odController,required: true,
commissionAmountController, keyboardType: TextInputType.numberWithOptions(decimal: true),
keyboardType: inputFormatters: decimalFormatter,
TextInputType.number, ),
), ),
), const SizedBox(width: 8),
], Expanded(
), child: _buildInput(
], 'PA',
paController,required: true,
keyboardType: TextInputType.numberWithOptions(decimal: true),
inputFormatters: decimalFormatter,
),
),
],
),
Row(
children: [
Expanded(
child: _buildInput(
'CGST',
cgstController,required: true,
keyboardType: TextInputType.numberWithOptions(decimal: true),
inputFormatters: decimalFormatter,
),
),
const SizedBox(width: 8),
Expanded(
child: _buildInput(
'SGST',
sgstController,required: true,
keyboardType: TextInputType.numberWithOptions(decimal: true),
inputFormatters: decimalFormatter,
),
),
const SizedBox(width: 8),
Expanded(
child: _buildInput(
'IGST',
igstController,required: true,
keyboardType: TextInputType.numberWithOptions(decimal: true),
inputFormatters: decimalFormatter,
),
),
],
),
],
),
), ),
), ),
),
const SizedBox( const SizedBox(height: 12),
height: 20,
), // spacing above fixed button // Vehicle Details
], Card(
elevation: 5,
color: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
child: Padding(
padding: const EdgeInsets.all(12.0),
child: Column(
crossAxisAlignment:
CrossAxisAlignment.stretch,
children: [
_sectionHeader(
'Vehicle Details',
icon: Icons.directions_car,
),
SizedBox(height: 10),
Row(
children: [
Expanded(
child: _buildInput(
'RTO State Name',
rtoStateCodeController,required: false,
),
),
const SizedBox(width: 8),
Expanded(
child: _buildInput(
'RTO City Code',
rtoCityCodeController,required: false,
keyboardType: TextInputType.number,
inputFormatters: digitsOnlyFormatter,
),
),
const SizedBox(width: 8),
Expanded(
child: _buildInput(
'Weight',
weightController,required: false,
keyboardType: TextInputType.number,
inputFormatters: digitsOnlyFormatter,
),
),
],
),
Row(
children: [
Expanded(
child: _buildInput(
'Fuel Type',
fuelTypeController,required: false,
),
),
const SizedBox(width: 8),
Expanded(
child: _buildInput(
'Date of Registration',
registrationDateController,required: false,
readOnly: true,
onTap: () => _pickDate(
registrationDateController,
context,
),
),
),
const SizedBox(width: 8),
Expanded(
child: _buildInput(
'Year of Manufacture',
yomController,required: false,
keyboardType: TextInputType.number,
inputFormatters: digitsOnlyFormatter,
),
),
],
),
Row(
children: [
Expanded(
child: _buildInput(
'Engine No',
engineNoController,required: false,
),
),
const SizedBox(width: 8),
Expanded(
child: _buildInput(
'Chassis No',
chassisNoController,required: false,
),
),
const SizedBox(width: 8),
Expanded(
child: Expanded(
child: _buildInput(
'Make',
makeController,required: false,
),
),
),
],
),
Row(
children: [
Expanded(
child: _buildInput(
'Model',
modelController,required: false,
),
),
const SizedBox(width: 8),
Expanded(
child: _buildInput(
'Cubic Capacity',
cubicCapacityController,required: false,
keyboardType: TextInputType.numberWithOptions(decimal: true),
inputFormatters: decimalFormatter,
),
),
const SizedBox(width: 8),
Expanded(
child: _buildInput(
'Vehicle Type',
vehicleTypeController,required: false,
),
),
],
),
],
),
),
),
const SizedBox(height: 12),
// Broker Details
Card(
elevation: 5,
color: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
child: Padding(
padding: const EdgeInsets.all(12.0),
child: Column(
crossAxisAlignment:
CrossAxisAlignment.stretch,
children: [
_sectionHeader(
'Broker / Commission',
icon: Icons.person,
),
SizedBox(height: 10),
Row(
children: [
Expanded(
child: _buildInput(
'Broker Name',
brokerNameController,required: false,
),
),
const SizedBox(width: 8),
Expanded(
child: _buildInput(
'Commission Amount',
commissionAmountController,required: false,
keyboardType: TextInputType.numberWithOptions(decimal: true),
inputFormatters: decimalFormatter,
),
),
],
),
],
),
),
),
const SizedBox(
height: 20,
), // spacing above fixed button
],
),
), ),
), ),
), ),
), ),
), )
], ],
), ),
), ),

View File

@ -700,26 +700,26 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: leak_tracker name: leak_tracker
sha256: "6bb818ecbdffe216e81182c2f0714a2e62b593f4a4f13098713ff1685dfb6ab0" sha256: "33e2e26bdd85a0112ec15400c8cbffea70d0f9c3407491f672a2fad47915e2de"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "10.0.9" version: "11.0.2"
leak_tracker_flutter_testing: leak_tracker_flutter_testing:
dependency: transitive dependency: transitive
description: description:
name: leak_tracker_flutter_testing name: leak_tracker_flutter_testing
sha256: f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573 sha256: "1dbc140bb5a23c75ea9c4811222756104fbcd1a27173f0c34ca01e16bea473c1"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.0.9" version: "3.0.10"
leak_tracker_testing: leak_tracker_testing:
dependency: transitive dependency: transitive
description: description:
name: leak_tracker_testing name: leak_tracker_testing
sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" sha256: "8d5a2d49f4a66b49744b23b018848400d23e54caf9463f4eb20df3eb8acb2eb1"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.0.1" version: "3.0.2"
lints: lints:
dependency: transitive dependency: transitive
description: description:
@ -1297,10 +1297,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: test_api name: test_api
sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd sha256: "522f00f556e73044315fa4585ec3270f1808a4b186c936e612cab0b565ff1e00"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.7.4" version: "0.7.6"
toastification: toastification:
dependency: "direct main" dependency: "direct main"
description: description:
@ -1417,10 +1417,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: vector_math name: vector_math
sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.1.4" version: "2.2.0"
vm_service: vm_service:
dependency: transitive dependency: transitive
description: description: