UI_ENQ_AGENT

This commit is contained in:
venbaittech 2025-09-24 12:57:48 +05:30
parent 720a27985a
commit 9c83ab630d
17 changed files with 3427 additions and 1119 deletions

View File

@ -91,13 +91,15 @@ class ApiService {
}
}
Future<void> getPdfDownload(path, id) async {
Future<void> getPdfDownload1(path, id) async {
// final url = Uri.parse(
// 'https://venbait.in/nhance/partner/dev/api/agent/downloadAgentCertificateFile?agent_id=1',
// );
final url = Uri.parse('https://venbait.in/nhance/partner/dev/$path');
// final token = await getToken();
print('GetDownload');
final url = Uri.parse('https://venbait.in/nhance/partner/dev/$path');
print('GetDownload - $url');
await _initializeToken();
if (_token == null) {
throw Exception('Token not found. Please log in.');
@ -130,28 +132,84 @@ class ApiService {
throw Exception('Error parsing response: $e');
}
} else if (response.statusCode == 404) {
// showDialog(
// context: context,
// builder: (BuildContext context) {
// return AlertDialog(
// title: Text('File not found.'),
// // content: Text('File not found.'),
// actions: [
// TextButton(
// child: Text('OK'),
// onPressed: () {
// Navigator.of(context).pop(); // Close the dialog
// },
// ),
// ],
// );
// },
// );
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text('File not found.'),
// content: Text('File not found.'),
actions: [
TextButton(
child: Text('OK'),
onPressed: () {
Navigator.of(context).pop(); // Close the dialog
},
),
],
);
},
);
} else {
throw Exception('Failed to load plans');
}
}
Future<void> getPdfDownload(String path, String id) async {
print('getPdfDownload 1');
final url = Uri.parse('https://venbait.in/nhance/partner/dev/$path');
await _initializeToken();
print('getPdfDownload 2');
if (_token == null) throw Exception('Token not found. Please log in.');
final headers = {
'Authorization': 'Bearer $_token',
'App-Signature': 'nhance-partner-2025-signature-35468846JRhH551HK',
};
final response = await _makeGethttpRequest(url, headers);
print('getPdfDownload 3');
if (response.statusCode == 200) {
final blob = html.Blob([response.bodyBytes]);
final blobUrl = html.Url.createObjectUrlFromBlob(blob);
print('getPdfDownload $blobUrl');
// --- filename resolution ---
String fileName = 'download_$id';
print('getPdfDownloadfileName $fileName');
final contentDisp = response.headers['content-disposition'];
print('getPdfDownloadcontentDisp $contentDisp');
if (contentDisp != null && contentDisp.contains('filename=')) {
print('getPdfDownloadcontentDisp..1');
fileName = contentDisp.split('filename=')[1].replaceAll('"', '');
print('getPdfDownloadcontentDisp..2');
} else {
print('getPdfDownloadcontentDisp..3');
// fallback: URL or id
fileName = path.split('/').last;
if (id != null) fileName = '${id}_$fileName';
}
final anchor = html.AnchorElement(href: blobUrl)
..setAttribute('download', fileName)
..click();
print('getPdfDownloadcontentDisp..4');
html.Url.revokeObjectUrl(blobUrl);
} else if (response.statusCode == 404) {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: const Text('File not found.'),
actions: [
TextButton(
child: const Text('OK'),
onPressed: () => Navigator.of(context).pop(),
),
],
),
);
} else {
throw Exception('Failed to download file');
}
}
Future<void> downloadFile({
required String? apiUrl,
required PlatformFile? localFile,
@ -176,7 +234,8 @@ class ApiService {
}
} else if (apiUrl != null) {
print("Download from API: $apiUrl");
await getPdfDownload(apiUrl, apiId);
print("Download from API: $apiId!");
await getPdfDownload(apiUrl, apiId!);
} else {
print("⚠️ No file available to download");
}
@ -516,6 +575,21 @@ class ApiService {
return response;
}
Future<Map<String, dynamic>> findSingleClaimData(id) async {
// print(_token);
if (_token == null) {
await _initializeToken();
}
final url = Uri.parse('${Env.apiUrl}claim/ClaimList?policy_number=$id');
final headers = {
'Authorization': 'Bearer $_token' ?? '',
'App-Signature': 'nhance-partner-2025-signature-35468846JRhH551HK',
};
final response = await _makeGetRequest(url, headers);
return response;
}
// ----------------------------------- ENDORSEMENT -------------------------------------------------
Future<Map<String, dynamic>> fetchEndorsementList(int id, role) async {
@ -549,6 +623,23 @@ class ApiService {
return response;
}
Future<Map<String, dynamic>> findSingleEnrosmentData(id) async {
// print(_token);
if (_token == null) {
await _initializeToken();
}
final url = Uri.parse(
'${Env.apiUrl}endorsement/endorsementList?policy_number=$id',
);
final headers = {
'Authorization': 'Bearer $_token' ?? '',
'App-Signature': 'nhance-partner-2025-signature-35468846JRhH551HK',
};
final response = await _makeGetRequest(url, headers);
return response;
}
// --------------------------------- MASTER DATA ----------------------------------------------
Future<Map<String, dynamic>> fetchMasterDropDown(String val) async {

View File

@ -3,7 +3,8 @@ import 'package:flutter/cupertino.dart';
class Validators {
static String? requiredField(String? value, String label) {
if (value == null || value.trim().isEmpty) {
return "$label is required";
return "Required";
// return "$label is required";
}
return null;
}

View File

@ -1,7 +1,9 @@
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:http/http.dart' as ref;
import 'package:nhance_partner/core/routing/routes.dart';
import 'package:nhance_partner/data/services/auth_service.dart';
import '../providers/manager_provider.dart';
import '../screens/UserManagement/Profile/profile_web.dart';
import '../widgets/custom_action_popup.dart';
import '../widgets/topbar.dart';
@ -26,7 +28,7 @@ class MainLayout extends StatelessWidget {
title: title,
isMobile: true,
onBack: () {
Navigator.pop(context);
// Navigator.pop(context);
},
onNotifications: () {
debugPrint("Notifications tapped");
@ -42,7 +44,8 @@ class MainLayout extends StatelessWidget {
} else if (index == 1) {
context.go(AppRoutes.profile);
} else if (index == 2) {
context.go(AppRoutes.tabEnquiry);
// ref.read(enquiryIdProvider.notifier).state = null;
context.go(AppRoutes.enquiryLst);
}
},
),

View File

@ -65,6 +65,8 @@ class EnquiryTabState extends ConsumerState<EnquiryTab> {
String? selectedIdProof;
String? selectedPrevPolicy;
String? selectedRCFileName;
String? rcFileUrlFromApi;
String? idProofFileUrlFromApi;
String? prevPolicyFileUrlFromApi;
@ -73,7 +75,7 @@ class EnquiryTabState extends ConsumerState<EnquiryTab> {
bool isLoading = false;
String? selectedVehicleType;
int? selectedVehicleTypeId;
// int? selectedVehicleTypeId;
String? selectedInsurer;
Map<String, TextEditingController> controllers = {};
@ -222,13 +224,17 @@ class EnquiryTabState extends ConsumerState<EnquiryTab> {
selectedVehicleType = data['vehicle_type_id'] ?? '';
selectedInsurer = data['insurer_id'] ?? '';
String? apiDocPath = data["certificate_file_name"];
// RC FILE
String? rcPath = data["rc_file_name"];
if (rcPath != null && rcPath.isNotEmpty) {
selectedRCFile = rcPath.split('/').last; // UI filename
selectedRCFileName = rcPath.split('/').last;
rcFileUrlFromApi = rcPath; // API download URL
docUploadedRCFile =
rcFileUrlFromApi as PlatformFile?; // user has not re-uploaded yet
docUploadedRCFile = null;
// docUploadedRCFile =
// rcFileUrlFromApi as PlatformFile?; // user has not re-uploaded yet
} else {
selectedRCFile = null; // no UI file shown
rcFileUrlFromApi = null;
@ -240,7 +246,8 @@ class EnquiryTabState extends ConsumerState<EnquiryTab> {
if (idProofPath != null && idProofPath.isNotEmpty) {
selectedIdProof = idProofPath.split('/').last;
idProofFileUrlFromApi = idProofPath; // FIXED correct variable
docUploadedIDProof = idProofFileUrlFromApi as PlatformFile?;
docUploadedIDProof = null;
// docUploadedIDProof = idProofFileUrlFromApi as PlatformFile?;
} else {
selectedIdProof = null;
idProofFileUrlFromApi = null;
@ -253,7 +260,8 @@ class EnquiryTabState extends ConsumerState<EnquiryTab> {
selectedPrevPolicy = prevPolicyPath.split('/').last;
prevPolicyFileUrlFromApi =
prevPolicyPath; // FIXED correct variable
docUploadedPrevPolicy = prevPolicyFileUrlFromApi as PlatformFile?;
docUploadedPrevPolicy = null;
// docUploadedPrevPolicy = prevPolicyFileUrlFromApi as PlatformFile?;
} else {
selectedPrevPolicy = null;
prevPolicyFileUrlFromApi = null;
@ -271,6 +279,22 @@ class EnquiryTabState extends ConsumerState<EnquiryTab> {
final dataSet = dataDetails();
print("dataSetAgent - $dataSet");
print("managerId - $managerId ,userId - $userId ");
if ((docUploadedRCFile == null ||
(docUploadedRCFile?.bytes == null &&
docUploadedRCFile?.path == null)) &&
(rcFileUrlFromApi == null || rcFileUrlFromApi!.isEmpty)) {
ToastHelper.showErrorToast(context, "Please upload RC Document");
return;
}
if ((docUploadedIDProof == null ||
(docUploadedIDProof?.bytes == null &&
docUploadedIDProof?.path == null)) &&
(idProofFileUrlFromApi == null || idProofFileUrlFromApi!.isEmpty)) {
ToastHelper.showErrorToast(context, "Please upload ID Proof");
return;
}
createUserData(dataSet);
} else {
// isDisable = false;
@ -331,36 +355,6 @@ class EnquiryTabState extends ConsumerState<EnquiryTab> {
);
}
Future<void> attachFiles1(http.MultipartRequest request) async {
Future<void> addFile(PlatformFile? file, String fieldName) async {
if (file == null) return;
try {
if (file.bytes != null) {
final multipartFile = http.MultipartFile.fromBytes(
fieldName,
file.bytes!,
filename: file.name,
);
request.files.add(multipartFile);
} else if (file.path != null) {
final multipartFile = await http.MultipartFile.fromPath(
fieldName,
file.path!,
filename: file.name,
);
request.files.add(multipartFile);
}
print("📎 File attached: ${file.name}$fieldName");
} catch (e) {
print("❌ Failed to attach $fieldName: $e");
}
}
await addFile(docUploadedRCFile, 'rc_file_name');
await addFile(docUploadedIDProof, 'id_proof_file_name');
await addFile(docUploadedPrevPolicy, 'previous_policy_file_name');
}
Future<void> createUserData(Map<String, dynamic> userData) async {
final bool isUpdating =
widget.data != null && widget.id != 'tab' && widget.id != 'null';
@ -397,16 +391,17 @@ class EnquiryTabState extends ConsumerState<EnquiryTab> {
// request.fields[key] = value.toString();
// print("✅ Encoded travel_details2: ${request.fields[key]}");
// });
await attachFiles(request);
userData.forEach((key, value) {
if (key != 'certificate_file_name') {
if (key != 'rc_file_name' && key != 'id_proof_file_name') {
request.fields[key] = value.toString();
print("✅ Encoded $key: ${request.fields[key]}");
} else {
print('Something Missing..');
}
});
// attach files
await attachFiles(request);
// request.fields['agent_id'] = selectedId.toString();
@ -423,7 +418,13 @@ class EnquiryTabState extends ConsumerState<EnquiryTab> {
print("✅ Agent submitted successfully!");
ToastHelper.showSuccessToast(context, 'Saved Enquiry');
print("Response: ${response.body}");
context.go(AppRoutes.tabEnquiry);
if (isUpdating) {
context.go(AppRoutes.tabEnquiry);
} else {
ref.read(enquiryIdProvider.notifier).state = null;
context.go(AppRoutes.enquiryLst);
}
} else {
print("❌ Submission failed. Status: ${response.statusCode}");
print("Body: ${response.body}");
@ -432,9 +433,9 @@ class EnquiryTabState extends ConsumerState<EnquiryTab> {
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text("Agent Creation Failed"),
title: Text("Enquiry Creation Failed"),
content: Text(
"There was a problem in creating user. Please try again.",
"There was a problem in creating enquiry. Please try again.",
),
actions: [
TextButton(
@ -455,6 +456,7 @@ class EnquiryTabState extends ConsumerState<EnquiryTab> {
@override
Widget build(BuildContext context) {
bool isMobile = ResponsiveLayout.isMobile(context);
return Container(
// color: Colors.green,
decoration: BoxDecoration(
@ -463,14 +465,16 @@ class EnquiryTabState extends ConsumerState<EnquiryTab> {
),
width: MediaQuery.of(context).size.width,
// height: MediaQuery.of(context).size.height * 0.8,
padding: EdgeInsets.all(26.0),
padding: isMobile
? EdgeInsets.only(left: 14.0, right: 14.0)
: EdgeInsets.all(26.0),
child: Column(
children: [
// Row(children: [Expanded(child: buildFormFields(context))]),
Expanded(
child: SingleChildScrollView(child: buildFormFields(context)),
),
isMobile
? Row(children: [Expanded(child: buildFormFields(context))])
: Expanded(
child: SingleChildScrollView(child: buildFormFields(context)),
),
const SizedBox(height: 20),
Row(
@ -499,27 +503,29 @@ class EnquiryTabState extends ConsumerState<EnquiryTab> {
}
Widget buildFormFields(BuildContext context) {
bool isMobile = ResponsiveLayout.isMobile(context);
return Form(
key: _formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildResponsiveRow(context, buildName(context), buildEmail(context)),
if (!ResponsiveLayout.isMobile(context)) const SizedBox(height: 20),
isMobile ? SizedBox(height: 5) : SizedBox(height: 20),
_buildResponsiveRow(
context,
buildPhNumber(context),
buildId(context),
),
if (!ResponsiveLayout.isMobile(context)) const SizedBox(height: 20),
isMobile ? SizedBox(height: 5) : SizedBox(height: 20),
_buildResponsiveRow(
context,
buildVehicleType(context),
buildInsurer(context),
),
if (!ResponsiveLayout.isMobile(context)) const SizedBox(height: 20),
isMobile ? SizedBox(height: 5) : SizedBox(height: 20),
_buildResponsiveRow(
context,
@ -527,7 +533,7 @@ class EnquiryTabState extends ConsumerState<EnquiryTab> {
buildUploadIDDocument(context),
),
if (!ResponsiveLayout.isMobile(context)) const SizedBox(height: 20),
isMobile ? SizedBox(height: 5) : SizedBox(height: 20),
_buildResponsiveRow(
context,
buildUploadPolicyDocument(context),
@ -621,9 +627,10 @@ class EnquiryTabState extends ConsumerState<EnquiryTab> {
Widget buildName(BuildContext context) {
return buildResponsiveField(
label: "Full Name ",
label: "Full Name *",
field: ThemedFormField(
controller: controllers['name']!,
validator: (value) => Validators.requiredField(value, "name"),
txtwidth: ResponsiveLayout.isMobile(context)
? null
@ -634,7 +641,7 @@ class EnquiryTabState extends ConsumerState<EnquiryTab> {
Widget buildEmail(BuildContext context) {
return buildResponsiveField(
label: "Email",
label: "Email *",
field: ThemedFormField(
controller: controllers['email']!,
validator: (value) => Validators.email(value, "email"),
@ -647,7 +654,7 @@ class EnquiryTabState extends ConsumerState<EnquiryTab> {
Widget buildPhNumber(BuildContext context) {
return buildResponsiveField(
label: "Phone Number",
label: "Phone Number *",
field: ThemedFormField(
controller: controllers['mobile']!,
validator: (value) => Validators.phone(value, "phNumber"),
@ -660,7 +667,7 @@ class EnquiryTabState extends ConsumerState<EnquiryTab> {
Widget buildId(BuildContext context) {
return buildResponsiveField(
label: "Registration Number",
label: "Registration Number *",
field: ThemedFormField(
controller: controllers['regNo']!,
validator: (value) => Validators.requiredField(value, "regNo"),
@ -680,9 +687,12 @@ class EnquiryTabState extends ConsumerState<EnquiryTab> {
);
return buildResponsiveField(
label: "Vehicle Type",
label: "Vehicle Type *",
field: Container(
color: Colors.white,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(10.0),
),
width: ResponsiveLayout.isMobile(context)
? null
: MediaQuery.of(context).size.width * 0.26,
@ -693,6 +703,12 @@ class EnquiryTabState extends ConsumerState<EnquiryTab> {
items: (filter, infiniteScrollProps) {
return filteredVechicleData;
},
validator: (val) {
if (val == null) {
return "Required"; // error message
}
return null;
},
itemAsString: (val) => val['vehicle_type'].toString(),
compareFn: (item, selectedItem) =>
@ -756,9 +772,12 @@ class EnquiryTabState extends ConsumerState<EnquiryTab> {
orElse: () => {},
);
return buildResponsiveField(
label: "Insurer",
label: "Insurer *",
field: Container(
color: Colors.white,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(10.0),
),
width: ResponsiveLayout.isMobile(context)
? null
: MediaQuery.of(context).size.width * 0.26,
@ -773,6 +792,12 @@ class EnquiryTabState extends ConsumerState<EnquiryTab> {
itemAsString: (val) => val['name'].toString(), // what to show
compareFn: (item, selectedItem) =>
item['id'] == selectedItem['id'], // compare by id
validator: (val) {
if (val == null) {
return "Required"; // error message
}
return null;
},
decoratorProps: DropDownDecoratorProps(
decoration:
AppInputDecorations.dropdownDecoration(
@ -860,14 +885,14 @@ class EnquiryTabState extends ConsumerState<EnquiryTab> {
padding: const EdgeInsets.only(top: 5),
child: Row(
children: [
IconButton(
onPressed: onRemove,
icon: const Icon(
Icons.remove_circle_outline_rounded,
color: Colors.redAccent,
),
tooltip: 'Remove Upload',
),
// IconButton(
// onPressed: onRemove,
// icon: const Icon(
// Icons.remove_circle_outline_rounded,
// color: Colors.redAccent,
// ),
// tooltip: 'Remove Upload',
// ),
GestureDetector(
onTap: onDownload,
child: Container(
@ -904,7 +929,10 @@ class EnquiryTabState extends ConsumerState<EnquiryTab> {
Text(label, style: _textStyle),
const SizedBox(height: 8),
uploadWidget,
downloadRow,
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [downloadRow],
),
const SizedBox(height: 16),
],
);
@ -931,12 +959,12 @@ class EnquiryTabState extends ConsumerState<EnquiryTab> {
Widget buildUploadRCDocument(BuildContext context) {
return buildResponsiveUploadField(
label: "Upload RC Document",
label: "Upload RC Document *",
hintText: selectedRCFile,
onFileSelected: (fileName, file) {
setState(() {
docUploadedRCFile = file;
// selectedFileNames = fileName;
selectedRCFileName = fileName;
});
},
showDownload: docUploadedRCFile != null || rcFileUrlFromApi != null,
@ -944,7 +972,7 @@ class EnquiryTabState extends ConsumerState<EnquiryTab> {
setState(() {
docUploadedRCFile = null;
rcFileUrlFromApi = null;
// selectedFileNames = null;
selectedRCFileName = null;
});
},
onDownload: () => apiService.downloadFile(
@ -952,29 +980,17 @@ class EnquiryTabState extends ConsumerState<EnquiryTab> {
apiUrl:
'api/enquiry/downloadEnquiryFile?enquiry_id=$selectedId&file_type=rc',
apiId: null,
apiId: selectedId.toString(),
localFile: docUploadedRCFile,
fileName: 'RC',
),
);
}
// Widget buildUploadIDDocument(BuildContext context) {
// return buildResponsiveUploadField(
// label: "Upload ID Proof",
// hintText: selectedIdProof,
// onFileSelected: (fileName, file) {
// print("File picked: $fileName");
// setState(() {
// docUploadedIDProof = file;
// });
// },
// );
// }
Widget buildUploadIDDocument(BuildContext context) {
return buildResponsiveUploadField(
label: "Upload ID Proof",
label: "Upload ID Proof *",
hintText: selectedIdProof,
onFileSelected: (fileName, file) {
setState(() {
@ -995,26 +1011,13 @@ class EnquiryTabState extends ConsumerState<EnquiryTab> {
apiUrl:
'api/enquiry/downloadEnquiryFile?enquiry_id=$selectedId&file_type=id_proof',
apiId: null,
localFile: docUploadedRCFile,
apiId: selectedId.toString(),
localFile: docUploadedIDProof,
fileName: 'Id_Proof',
),
);
}
// Widget buildUploadPolicyDocument(BuildContext context) {
// return buildResponsiveUploadField(
// label: "Upload Previous Policy",
// hintText: selectedPrevPolicy,
// onFileSelected: (fileName, file) {
// print("File picked: $fileName");
// setState(() {
// docUploadedPrevPolicy = file;
// });
// },
// );
// }
Widget buildUploadPolicyDocument(BuildContext context) {
return buildResponsiveUploadField(
label: "Upload Previous Policy",
@ -1039,7 +1042,7 @@ class EnquiryTabState extends ConsumerState<EnquiryTab> {
apiUrl:
'api/enquiry/downloadEnquiryFile?enquiry_id=$selectedId&file_type=previous_policy',
apiId: null,
apiId: selectedId.toString(),
localFile: docUploadedPrevPolicy,
fileName: 'Previous_Policy',
),
@ -1052,6 +1055,7 @@ class EnquiryTabState extends ConsumerState<EnquiryTab> {
field: ThemedFormField(
maxLength: 500,
controller: controllers['remarks']!,
keyboardType: TextInputType.multiline,
txtwidth: ResponsiveLayout.isMobile(context)
? null
: MediaQuery.of(context).size.width * 0.26,

View File

@ -1,52 +1,588 @@
import 'dart:convert';
import 'package:dropdown_search/dropdown_search.dart';
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:http/http.dart' as http;
import 'package:toastification/toastification.dart';
import 'package:flutter/material.dart';
import '../../../../core/config/env.dart';
import '../../../../core/services/api_service.dart';
import '../../../../data/services/auth_service.dart';
import '../../../../data/utils/toastNotification.dart';
import '../../../../data/utils/validators.dart';
import '../../../layouts/responsive_layout.dart';
import '../../../themes/indicators/input_field_decoration.dart';
import '../../../themes/indicators/text_field_theme.dart';
Future<void> showAddDialog({
required BuildContext context,
required String title,
required void Function(String value) onSubmit,
}) async {
final TextEditingController controller = TextEditingController();
// 🔹 Custom Dialog Widget
class AddDialog extends StatefulWidget {
final String title;
final dynamic userId;
final void Function(String value) onSubmit;
final dynamic policyNumber;
const AddDialog({
super.key,
required this.title,
required this.onSubmit,
required this.userId,
this.policyNumber,
});
await showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: Text(title),
content: title == 'Claim'
? claims()
: endorsement(), // 👈 conditional content
actions: [
TextButton(
onPressed: () => Navigator.of(context).pop(),
child: const Text("Cancel"),
),
ElevatedButton(
onPressed: () {
onSubmit(controller.text.trim());
Navigator.of(context).pop();
},
child: const Text("Save"),
),
],
@override
State<AddDialog> createState() => _AddDialogState();
}
class _AddDialogState extends State<AddDialog> {
late ApiService apiService;
String? _token;
bool isLoading = false;
late TextEditingController controller;
Map<String, TextEditingController> controllers = {};
final _formKeyClaims = GlobalKey<FormState>();
final _formKeyEndrosment = GlobalKey<FormState>();
final GlobalKey<DropdownSearchState<Map<String, dynamic>>> dropDownKey =
GlobalKey<DropdownSearchState<Map<String, dynamic>>>();
final GlobalKey<DropdownSearchState<Map<String, dynamic>>>
dropDownKeyEndorsement =
GlobalKey<DropdownSearchState<Map<String, dynamic>>>();
List<String> tabHeader = ['policyNum', 'claimsDesc', 'remarks'];
List<Map<String, dynamic>> getClaimsTypeData = [];
List<Map<String, dynamic>> filteredClaimsData = [];
List<Map<String, dynamic>> getEndrosmentType = [];
List<Map<String, dynamic>> filteredEndrosmentData = [];
String? selectedClaimsType;
String? selectedEndorsement;
Map<String, dynamic> claimsDetails() {
final data = {
"policy_number": controllers["policyNum"]?.text,
"claim_description": controllers["claimsDesc"]?.text,
"claim_type": selectedClaimsType,
// "manager_id": managerId,
"created_by": widget.userId,
};
return data;
}
Map<String, dynamic> endrosmentDetails() {
final data = {
"policy_number": controllers["policyNum"]?.text,
"endorsement_description": controllers["remarks"]?.text,
"endorsement_type": selectedEndorsement,
// "manager_id": managerId,
"created_by": widget.userId,
};
return data;
}
@override
void initState() {
super.initState();
apiService = ApiService();
for (String field in tabHeader) {
controllers[field] = TextEditingController();
}
controllers["policyNum"]?.text = widget.policyNumber;
// 🔹 Init logic here (API calls, token fetch, etc.)
_initializeToken();
getClaimsType();
getEnroementType();
}
Future<void> _initializeToken() async {
_token = await AuthService.getToken();
print("APISERTOKEN - $_token");
}
Future<void> getClaimsType() async {
print('getClaimList called');
setState(() {
isLoading = true;
});
try {
final response = await apiService.fetchMasterDropDown('Claim');
if (response['status'] == 200) {
print('getClaimsTypeData - ${response['data']}');
setState(() {
getClaimsTypeData = List<Map<String, dynamic>>.from(response['data']);
print('API Data - $getClaimsTypeData');
filteredClaimsData = List.from(getClaimsTypeData);
print('originalData - $filteredClaimsData');
});
} else {
getClaimsTypeData = [];
filteredClaimsData = [];
}
} catch (e) {
print('Exception occurred: $e');
} finally {
setState(() {
isLoading = false;
});
}
}
Future<void> getEnroementType() async {
print('Insurers called');
setState(() {
isLoading = true;
});
try {
final response = await apiService.fetchMasterDropDown('Endorsement');
if (response['status'] == 200) {
print('getEnroementType - ${response['data']}');
setState(() {
getEndrosmentType = List<Map<String, dynamic>>.from(response['data']);
print('API Data - $getEndrosmentType');
filteredEndrosmentData = List.from(getEndrosmentType);
print('originalData - $filteredEndrosmentData');
});
} else {
getEndrosmentType = [];
filteredEndrosmentData = [];
}
} catch (e) {
print('Exception occurred: $e');
} finally {
setState(() {
isLoading = false;
});
}
}
void handleDone(val) {
print('val - $val');
if (val == 'Claims') {
if (!_formKeyClaims.currentState!.validate()) return;
setState(() {
if (_formKeyClaims.currentState!.validate()) {
claimsDetails();
final dataSet = claimsDetails();
print("dataSetAgent - $dataSet");
// print("managerId - $managerId ,userId - $userId ");
createUserData(dataSet, val);
} else {
// isDi sable = false;
}
});
} else {
if (!_formKeyEndrosment.currentState!.validate()) return;
setState(() {
if (_formKeyEndrosment.currentState!.validate()) {
endrosmentDetails();
final dataSet = endrosmentDetails();
print("dataSetAgent - $dataSet");
// print("managerId - $managerId ,userId - $userId ");
createUserData(dataSet, val);
} else {
// isDisable = false;
}
});
}
}
Future<void> createUserData(data, val) async {
// final bool isUpdating = widget.id != null && widget.id != 'create';
final String apiUrldata;
apiUrldata = (val == 'Claims')
? '${Env.apiUrl}claim/createClaim'
: '${Env.apiUrl}endorsement/createEndorsement';
// final token = await getToken(); // Fetch token
if (_token == null) {
throw Exception('Token not found. Please log in.');
}
print("data------- $data}");
try {
final response = await http.post(
Uri.parse(apiUrldata),
headers: {
'Authorization': 'Bearer $_token',
'Content-Type': 'application/json',
'app-signature': 'nhance-partner-2025-signature-35468846JRhH551HK',
},
body: jsonEncode(data), // Convert map to JSON
);
},
);
}
Widget claims() {
return Container(
padding: const EdgeInsets.all(16),
color: Colors.redAccent,
child: const Text('Claims', style: TextStyle(color: Colors.white)),
);
}
if (response.statusCode == 200) {
print("Staff submitted successfully!");
print("Response: ${response.body}");
ToastHelper.showSuccessToast(context, 'Saved Successfully');
Navigator.of(context).pop();
Widget endorsement() {
return Container(
padding: const EdgeInsets.all(16),
color: Colors.yellow,
child: const Text('Endorsement', style: TextStyle(color: Colors.black)),
widget.onSubmit("success");
// context.go(AppRoutes.staffLst);
} else {
final responseBody = jsonDecode(response.body);
dynamic msg = responseBody['data'];
print("Failed to submit plan. Status: ${response.statusCode}");
print("Error: ${response.body}");
ToastHelper.showErrorToast(context, 'Failed To Save');
}
} catch (e) {
print(" Error submitting Staff: $e");
}
}
@override
void dispose() {
// controllers.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return AlertDialog(
backgroundColor: Colors.white,
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
// Header row
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
widget.title,
style: GoogleFonts.inter(
color: const Color(0xFF374141),
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
GestureDetector(
onTap: () => Navigator.pop(context),
child: Container(
padding: const EdgeInsets.all(5.0),
decoration: BoxDecoration(
color: const Color(0xFFF1F1F1),
borderRadius: BorderRadius.circular(5.0),
),
child: const Icon(Icons.close, size: 18),
),
),
],
),
const SizedBox(height: 16),
// 🔹 Switch content dynamically
widget.title == 'Claims' ? claims(context) : endorsement(context),
],
),
actions: [
Center(
child: GestureDetector(
onTap: () {
handleDone(widget.title);
// widget.onSubmit(controller.text.trim());
// Navigator.of(context).pop();
},
child: Container(
padding: const EdgeInsets.symmetric(
horizontal: 25.0,
vertical: 8,
),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8.0),
color: const Color(0xFF425B5B),
),
child: const Text('Done', style: TextStyle(color: Colors.white)),
),
),
),
],
);
}
// 🔹 Example: Claims widget
Widget claims(BuildContext context) {
return Form(
key: _formKeyClaims,
child: Column(
children: [
buildClaimType(context),
buildPolicyNumber(context),
buildClaimsDesc(context),
],
),
);
}
// 🔹 Example: Endorsement widget
Widget endorsement(BuildContext context) {
return Form(
key: _formKeyEndrosment,
child: Column(
children: [buildEndrosType(context), buildEndrosRemarks(context)],
),
);
}
// ------------------------- Claims Part -----------------------------------
Widget buildPolicyNumber(context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text("Policy Number", style: _textStyle),
SizedBox(height: 10),
ThemedFormField(
controller: controllers['policyNum']!,
backgroundColor: Color(0xFFEDF6F5),
validator: (value) => Validators.requiredField(value, "name"),
txtwidth: ResponsiveLayout.isMobile(context)
? null
: MediaQuery.of(context).size.width * 0.26,
),
],
);
}
Widget buildClaimsDesc(context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text("Claims Description", style: _textStyle),
SizedBox(height: 10),
ThemedFormField(
backgroundColor: Color(0xFFEDF6F5),
controller: controllers['claimsDesc']!,
validator: (value) => Validators.requiredField(value, "claimsDesc"),
txtwidth: ResponsiveLayout.isMobile(context)
? null
: MediaQuery.of(context).size.width * 0.26,
keyboardType: TextInputType.multiline,
),
],
);
}
Widget buildClaimType(context) {
Map<String, dynamic>? selectedVehicle = filteredClaimsData.firstWhere(
(item) => item['id'].toString() == selectedClaimsType,
orElse: () => {},
);
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text("Claims Type", style: _textStyle),
SizedBox(height: 10),
Container(
color: Colors.white,
width: ResponsiveLayout.isMobile(context)
? null
: MediaQuery.of(context).size.width * 0.26,
// height: 40,
child: DropdownSearch<Map<String, dynamic>>(
key: dropDownKey,
selectedItem: selectedVehicle.isNotEmpty ? selectedVehicle : null,
items: (filter, infiniteScrollProps) {
return filteredClaimsData;
},
itemAsString: (val) => val['claim_type'].toString(),
compareFn: (item, selectedItem) =>
item['id'] == selectedItem['id'], // compare by id
validator: (val) {
if (val == null) {
return "Required"; // error message
}
return null;
},
decoratorProps: DropDownDecoratorProps(
decoration:
AppInputDecorations.dropdownDecoration(
label: "Select Claims Type",
).copyWith(
filled: true,
fillColor: Color(
0xFFEDF6F5,
), // 👈 makes the dropdown input white
),
),
popupProps: PopupProps.menu(
fit: FlexFit.loose,
constraints: BoxConstraints(maxHeight: 250),
menuProps: MenuProps(
backgroundColor:
Colors.white, // 👈 sets dropdown background to white
),
showSearchBox: true,
searchFieldProps: TextFieldProps(
decoration: InputDecoration(
filled: true,
fillColor: Colors.white,
hintText: "Search Claims Type...",
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(
color: Colors.white,
), // 👈 Normal border
),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(
color: Colors.white,
width: 1.5,
), // 👈 Focused border
),
),
),
// constraints: BoxConstraints(),
),
onChanged: (val) {
if (val != null) {
print("Selected ClaimsType : ${val['claim_type']}");
print("Id: ${val['id']}");
selectedClaimsType = val['id'];
// controllers['agentId']?.text = val['agent_code'];
// agentId = agent['id'];
}
},
),
),
],
);
}
// ------------------------- Endrosment Part -----------------------------------
Widget buildEndrosType(context) {
Map<String, dynamic>? selectedEndorsementd = filteredEndrosmentData
.firstWhere(
(item) => item['id'].toString() == selectedEndorsement,
orElse: () => {},
);
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text("Endorsement Type", style: _textStyle),
SizedBox(height: 10),
Container(
color: Colors.white,
width: ResponsiveLayout.isMobile(context)
? null
: MediaQuery.of(context).size.width * 0.26,
// height: 40,
child: DropdownSearch<Map<String, dynamic>>(
key: dropDownKeyEndorsement,
selectedItem: selectedEndorsementd.isNotEmpty
? selectedEndorsementd
: null,
items: (filter, infiniteScrollProps) {
return filteredEndrosmentData;
},
itemAsString: (val) =>
val['endorsement_type'].toString(), // what to show
compareFn: (item, selectedItem) =>
item['id'] == selectedItem['id'], // compare by id
validator: (val) {
if (val == null) {
return "Required"; // error message
}
return null;
},
decoratorProps: DropDownDecoratorProps(
decoration:
AppInputDecorations.dropdownDecoration(
label: "Select Endorsement Type",
).copyWith(
filled: true,
fillColor: Color(
0xFFEDF6F5,
), // 👈 makes the dropdown input white
),
),
popupProps: PopupProps.menu(
fit: FlexFit.loose,
constraints: BoxConstraints(maxHeight: 250),
menuProps: MenuProps(
backgroundColor:
Colors.white, // 👈 sets dropdown background to white
),
showSearchBox: true,
searchFieldProps: TextFieldProps(
decoration: InputDecoration(
filled: true,
fillColor: Colors.white,
hintText: "Search Endorsement Type...",
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(
color: Colors.white,
), // 👈 Normal border
),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(
color: Colors.white,
width: 1.5,
), // 👈 Focused border
),
),
),
// constraints: BoxConstraints(),
),
onChanged: (val) {
if (val != null) {
print("Selected Endorsement : ${val['endorsement_type']}");
print("Id: ${val['id']}");
selectedEndorsement = val['id'];
// controllers['agentId']?.text = val['agent_code'];
// agentId = agent['id'];
}
},
),
),
],
);
}
Widget buildEndrosRemarks(context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text("Remarks", style: _textStyle),
SizedBox(height: 10),
ThemedFormField(
backgroundColor: Color(0xFFEDF6F5),
controller: controllers['remarks']!,
validator: (value) => Validators.requiredField(value, "remarks"),
txtwidth: ResponsiveLayout.isMobile(context)
? null
: MediaQuery.of(context).size.width * 0.26,
keyboardType: TextInputType.multiline,
),
],
);
}
// ------------------- STyle ---------------------------------
static final TextStyle _textStyle = TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
);
}

View File

@ -15,16 +15,26 @@ import '../../../providers/manager_provider.dart';
import '../../../themes/indicators/export_btn.dart';
import '../../../themes/indicators/search_field_theme.dart';
import '../../../themes/indicators/text_field_theme.dart';
import '../../../widgets/custom_action_popup.dart';
import '../policy_claims_endros/sub_claims.dart';
import '../policy_claims_endros/sub_endrosment.dart';
class PolicyTab extends ConsumerStatefulWidget {
final List<Map<String, dynamic>>? data;
dynamic id;
// const PolicyTab({super.key});
const PolicyTab({super.key, this.data});
PolicyTab({super.key, this.data, this.id});
@override
ConsumerState<PolicyTab> createState() => PolicyTabState();
}
class PolicyTabState extends ConsumerState<PolicyTab> {
final GlobalKey<ClaimsSubListState> claimsKey =
GlobalKey<ClaimsSubListState>();
final GlobalKey<EndrosmentSubListState> enrollKey =
GlobalKey<EndrosmentSubListState>();
List<Map<String, dynamic>> dataVal = [];
final TextEditingController _searchController = TextEditingController();
@ -38,13 +48,6 @@ class PolicyTabState extends ConsumerState<PolicyTab> {
bool isLoading = false;
List<String> tabHeader = [
'name',
'email',
'mobile',
'code',
'address',
'regNo',
'regNum',
'insurer',
'idv',
@ -59,7 +62,7 @@ class PolicyTabState extends ConsumerState<PolicyTab> {
String? _token;
dynamic userId;
dynamic managerId;
dynamic selectedPolicyNumber;
@override
void initState() {
super.initState();
@ -74,7 +77,7 @@ class PolicyTabState extends ConsumerState<PolicyTab> {
managerId = ref.watch(managerIdProvider);
userId = ref.watch(userIdProvider);
getQuotationList(managerId);
getPolicyList(managerId);
});
}
@ -83,8 +86,8 @@ class PolicyTabState extends ConsumerState<PolicyTab> {
print("APISERTOKEN - $_token");
}
Future<void> getQuotationList(int managerId) async {
print('getClaimList called MagId - $managerId');
Future<void> getPolicyList(int managerId) async {
print('getPolicyList called MagId - $managerId');
setState(() {
isLoading = true;
});
@ -101,11 +104,14 @@ class PolicyTabState extends ConsumerState<PolicyTab> {
// getQuotationData = List<Map<String, dynamic>>.from(response['data']);
originalData = getQuotationData;
filteredData = List.from(originalData);
// print('originalData - $getClaimPolicies');
updateData(filteredData);
});
} else {
getQuotationData = [];
originalData = [];
filteredData = [];
}
} catch (e) {
print('Exception occurred: $e');
@ -116,228 +122,329 @@ class PolicyTabState extends ConsumerState<PolicyTab> {
}
}
void updateData(data) {
if (data != null && data is List && data.isNotEmpty) {
final record = data[0]; // get the first map
print('UPDID - $record');
setState(() {
selectedPolicyNumber = record['policy_number']?.toString() ?? '';
controllers['regNum']?.text = record['reg_no']?.toString() ?? '';
controllers['insurer']?.text = record['insurer_name']?.toString() ?? '';
controllers['idv']?.text =
record['insured_declared_value']?.toString() ?? '';
controllers['insurerName']?.text =
record['insured_name']?.toString() ?? '';
controllers['policyNo']?.text =
record['policy_number']?.toString() ?? '';
controllers['paymentMode']?.text =
record['payment_mode']?.toString() ?? '';
controllers['premAmount']?.text =
record['premium_amount']?.toString() ?? '';
controllers['planType']?.text =
record['insurance_plan_type']?.toString() ?? '';
});
}
}
@override
Widget build(BuildContext context) {
return Container(
height: MediaQuery.of(context).size.height,
width: MediaQuery.of(context).size.width,
child: SingleChildScrollView(
scrollDirection: Axis.vertical,
child: Column(
children: [
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: [
Container(
child: Row(
final isMobile = ResponsiveLayout.isMobile(context);
return isLoading
? CircularProgressIndicator()
: Container(
height: MediaQuery.of(context).size.height,
width: MediaQuery.of(context).size.width,
child: filteredData.isNotEmpty
? SingleChildScrollView(
scrollDirection: Axis.vertical,
child: Column(
children: [
Text(
'Policy Details',
style: GoogleFonts.inter(
fontWeight: FontWeight.bold,
fontSize: 16,
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: isMobile
? EdgeInsets.all(10.0)
: EdgeInsets.all(16.0),
child: Column(
children: [
if (!ResponsiveLayout.isMobile(context)) ...[
Container(
child: Row(
children: [
Text(
'Policy Details',
style: GoogleFonts.inter(
fontWeight: FontWeight.bold,
fontSize: 16,
),
),
if (!ResponsiveLayout.isMobile(
context,
)) ...[
Spacer(),
buildDocPolicy(context, isMobile),
Spacer(),
GestureDetector(
onTap: () {},
child: Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(5),
color: const Color(0xFF425B5B),
),
child: Row(
children: const [
Text(
"Download Policy",
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w200,
color: Colors.white,
SizedBox(width: 15),
buildDocReciept(context, isMobile),
],
// GestureDetector(
// onTap: () {
// print("Upload ${widget.id}");
// final selectedId = widget.id;
// // final path =
// // 'api/agent/downloadAgentIncentiveFile?id=$selectedId';
// final path =
// 'api/policy/downloadPolicyFile?policy_id=$selectedId&file_type=policy_payment_receipt';
// apiService.getPdfDownload(path, selectedId);
// },
// child: Container(
// padding: const EdgeInsets.all(8),
// decoration: BoxDecoration(
// borderRadius: BorderRadius.circular(5),
// color: const Color(0xFF425B5B),
// ),
// child: Row(
// children: const [
// Text(
// "Download Receipt",
// style: TextStyle(
// fontSize: 12,
// fontWeight: FontWeight.w200,
// color: Colors.white,
// ),
// ),
// SizedBox(width: 15),
// Icon(
// Icons.file_download_outlined,
// size: 16,
// color: Colors.white,
// ),
// ],
// ),
// ),
// ),
],
),
),
SizedBox(width: 15),
Icon(
Icons.file_download_outlined,
size: 16,
color: Colors.white,
),
const SizedBox(height: 20),
],
),
SingleChildScrollView(
child: buildFormFields(context),
),
isMobile
? SizedBox(height: 5)
: SizedBox(height: 20),
],
),
),
SizedBox(width: 15),
GestureDetector(
onTap: () {},
child: Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(5),
color: const Color(0xFF425B5B),
),
child: Row(
children: const [
Text(
"Download Receipt",
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w200,
color: Colors.white,
),
isMobile ? SizedBox(height: 5) : SizedBox(height: 20),
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(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'More Information (optional)',
style: GoogleFonts.inter(
fontWeight: FontWeight.bold,
fontSize: 16,
),
SizedBox(width: 15),
Icon(
Icons.file_download_outlined,
size: 16,
color: Colors.white,
),
if (!ResponsiveLayout.isMobile(context)) ...[
SizedBox(height: 10),
Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(
width: 100,
child: Text('Claim', style: _headerStyle),
),
SizedBox(width: 20),
Expanded(
child: buildMoreInfoSetClaims(context),
),
],
),
],
),
if (ResponsiveLayout.isMobile(context))
Container(
padding: EdgeInsets.all(6.0),
decoration: BoxDecoration(
color: Color(0xFFF6FEFD),
// color: Color(0xFFCBE6E2),
borderRadius: BorderRadius.circular(10.0),
),
child: Column(
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
SizedBox(
width: 100,
child: Text(
'Claim',
style: _headerStyle,
),
),
_actionClaims(context),
],
),
SizedBox(width: 20),
buildMoreInfoSetClaims(context),
],
),
),
SizedBox(height: 10),
if (!ResponsiveLayout.isMobile(context))
Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(
width: 100,
child: Text(
'Endorsement',
style: _headerStyle,
),
),
SizedBox(width: 20),
Expanded(
child: buildMoreInfoSetEndrosment(
context,
),
),
],
),
if (ResponsiveLayout.isMobile(context))
Container(
padding: EdgeInsets.all(6.0),
decoration: BoxDecoration(
color: Color(0xFFF6FEFD),
// color: Color(0xFFCBE6E2),
borderRadius: BorderRadius.circular(10.0),
),
child: Column(
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
SizedBox(
width: 100,
child: Text(
'Endorsement',
style: _headerStyle,
),
),
_actionEnroll(context),
],
),
SizedBox(width: 20),
buildMoreInfoSetEndrosment(context),
],
),
),
// SizedBox(height: 10),
// 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(
// 'Save',
// style: TextStyle(color: Colors.white),
// ),
// ),
// ),
// ],
// ),
// SizedBox(height: 30),
// _buildDataTable(context),
],
),
),
],
),
),
const SizedBox(height: 20),
SingleChildScrollView(child: buildFormFields(context)),
)
: Center(child: Text('No Available Data')),
);
}
const SizedBox(height: 20),
],
Widget buildDocPolicy(BuildContext context, bool isMobile) {
return GestureDetector(
onTap: () {
print("Upload ${widget.id}");
final selectedId = widget.id;
// final path =
// 'api/agent/downloadAgentIncentiveFile?id=$selectedId';
final path =
'api/policy/downloadPolicyFile?policy_id=$selectedId&file_type=policy_pdf';
apiService.getPdfDownload(path, selectedId);
},
child: Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(5),
color: const Color(0xFF425B5B),
),
child: Row(
children: [
const Text(
"Download Policy",
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w200,
color: Colors.white,
),
),
const SizedBox(height: 20),
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(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('More Information', style: _headerStyle),
Row(
children: [
SizedBox(
width: 100,
child: Text('Claim', style: _headerStyle),
),
SizedBox(width: 20),
Expanded(
child: Container(
padding: EdgeInsets.all(6.0),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(10.0),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Container(child: buildClaimTable()),
IconButton(
onPressed: () {
showAddDialog(
context: context,
title: "Claim",
onSubmit: (value) {
print("New Claim: $value");
// update claim list here
},
);
},
icon: Icon(Icons.add),
),
],
),
),
),
],
),
SizedBox(height: 10),
Row(
children: [
SizedBox(
width: 100,
child: Text('Endorsement', style: _headerStyle),
),
SizedBox(width: 20),
Expanded(
child: Container(
padding: EdgeInsets.all(8.0),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(6.0),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Container(child: buildEndrosmentTable()),
IconButton(
onPressed: () {
showAddDialog(
context: context,
title: "Endorsement",
onSubmit: (value) {
print("New Claim: $value");
// update claim list here
},
);
},
icon: Icon(Icons.add),
),
],
),
),
),
],
),
SizedBox(height: 10),
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(
'Save',
style: TextStyle(color: Colors.white),
),
),
),
],
),
// SizedBox(height: 30),
// _buildDataTable(context),
],
),
isMobile ? const Spacer() : const SizedBox(width: 15),
const Icon(
Icons.file_download_outlined,
size: 16,
color: Colors.white,
),
],
),
@ -345,7 +452,149 @@ class PolicyTabState extends ConsumerState<PolicyTab> {
);
}
Widget buildDocReciept(BuildContext context, bool isMobile) {
return GestureDetector(
onTap: () {
print("Upload ${widget.id}");
final selectedId = widget.id;
// final path =
// 'api/agent/downloadAgentIncentiveFile?id=$selectedId';
final path =
'api/policy/downloadPolicyFile?policy_id=$selectedId&file_type=policy_payment_receipt';
apiService.getPdfDownload(path, selectedId);
},
child: Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(5),
color: const Color(0xFF425B5B),
),
child: Row(
children: [
Text(
"Download Receipt",
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w200,
color: Colors.white,
),
),
isMobile ? const Spacer() : const SizedBox(width: 15),
Icon(Icons.file_download_outlined, size: 16, color: Colors.white),
],
),
),
);
}
Widget buildMoreInfoSetClaims(BuildContext context) {
if (selectedPolicyNumber == null || selectedPolicyNumber!.isEmpty) {
return const Center(child: CircularProgressIndicator()); // or placeholder
}
return Container(
padding: ResponsiveLayout.isMobile(context) ? null : EdgeInsets.all(6.0),
decoration: ResponsiveLayout.isMobile(context)
? null
: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(10.0),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: ClaimsSubList(
key: claimsKey,
policyNumber: selectedPolicyNumber,
),
),
if (!ResponsiveLayout.isMobile(context)) _actionClaims(context),
],
),
);
}
Widget buildMoreInfoSetEndrosment(BuildContext context) {
if (selectedPolicyNumber == null || selectedPolicyNumber!.isEmpty) {
return const Center(child: CircularProgressIndicator()); // or placeholder
}
return Container(
// padding: EdgeInsets.all(8.0),
// decoration: BoxDecoration(
// color: Colors.white,
// borderRadius: BorderRadius.circular(6.0),
// ),
padding: ResponsiveLayout.isMobile(context) ? null : EdgeInsets.all(6.0),
decoration: ResponsiveLayout.isMobile(context)
? null
: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(10.0),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: EndrosmentSubList(
key: enrollKey,
policyNumber: selectedPolicyNumber,
),
),
if (!ResponsiveLayout.isMobile(context)) _actionEnroll(context),
],
),
);
}
Widget _actionEnroll(BuildContext context) {
return IconButton(
onPressed: () {
showDialog(
context: context,
builder: (ctx) => AddDialog(
userId: userId,
title: "Endorsement",
policyNumber: selectedPolicyNumber,
onSubmit: (value) {
debugPrint("New Endorsement: $value");
enrollKey.currentState?.getEndrosmentList(selectedPolicyNumber);
// Update claim list or call API
},
),
);
},
icon: Icon(Icons.add),
);
}
Widget _actionClaims(BuildContext context) {
return IconButton(
onPressed: () {
showDialog(
context: context,
builder: (ctx) => AddDialog(
userId: userId,
title: "Claims",
policyNumber: selectedPolicyNumber,
onSubmit: (value) {
debugPrint("New Claim: $value");
claimsKey.currentState?.getClaimsList(selectedPolicyNumber);
// Update claim list or call API
},
),
);
},
icon: Icon(Icons.add),
);
}
Widget buildFormFields(BuildContext context) {
final isMobile = ResponsiveLayout.isMobile(context);
return Form(
key: _formKey,
child: Column(
@ -356,28 +605,36 @@ class PolicyTabState extends ConsumerState<PolicyTab> {
buildName(context),
buildInsusrer(context),
),
if (!ResponsiveLayout.isMobile(context)) const SizedBox(height: 10),
isMobile ? SizedBox(height: 5) : SizedBox(height: 10),
_buildResponsiveRow(
context,
buildIdv(context),
buildInsurdName(context),
),
if (!ResponsiveLayout.isMobile(context)) const SizedBox(height: 10),
isMobile ? SizedBox(height: 5) : SizedBox(height: 10),
_buildResponsiveRow(
context,
buildPolicyNum(context),
buildPaymentMode(context),
),
if (!ResponsiveLayout.isMobile(context)) const SizedBox(height: 10),
isMobile ? SizedBox(height: 5) : SizedBox(height: 10),
_buildResponsiveRow(
context,
buildPlanType(context),
buildPremiumAmount(context),
),
isMobile ? SizedBox(height: 5) : SizedBox(height: 10),
if (ResponsiveLayout.isMobile(context))
_buildResponsiveRow(
context,
buildDocPolicy(context, isMobile),
buildDocReciept(context, isMobile),
),
],
),
);
@ -390,7 +647,7 @@ class PolicyTabState extends ConsumerState<PolicyTab> {
) {
if (ResponsiveLayout.isMobile(context)) {
// Stack vertically
return Column(children: [first, const SizedBox(height: 2), second]);
return Column(children: [first, const SizedBox(height: 4), second]);
} else {
// Place side by side
return Row(
@ -432,8 +689,8 @@ class PolicyTabState extends ConsumerState<PolicyTab> {
return buildResponsiveField(
label: "Registration Number",
field: ThemedFormField(
controller: controllers['name']!,
validator: (value) => Validators.requiredField(value, "name"),
controller: controllers['regNum']!,
// validator: (value) => Validators.requiredField(value, "name"),
txtwidth: ResponsiveLayout.isMobile(context)
? null
: MediaQuery.of(context).size.width * 0.26,
@ -445,8 +702,7 @@ class PolicyTabState extends ConsumerState<PolicyTab> {
return buildResponsiveField(
label: "Insusrer",
field: ThemedFormField(
controller: controllers['email']!,
validator: (value) => Validators.email(value, "email"),
controller: controllers['insurer']!,
txtwidth: ResponsiveLayout.isMobile(context)
? null
: MediaQuery.of(context).size.width * 0.26,
@ -458,7 +714,7 @@ class PolicyTabState extends ConsumerState<PolicyTab> {
return buildResponsiveField(
label: "IDV",
field: ThemedFormField(
controller: controllers['mobile']!,
controller: controllers['idv']!,
validator: (value) => Validators.phone(value, "phNumber"),
txtwidth: ResponsiveLayout.isMobile(context)
? null
@ -471,8 +727,8 @@ class PolicyTabState extends ConsumerState<PolicyTab> {
return buildResponsiveField(
label: "Insured Name",
field: ThemedFormField(
controller: controllers['mobile']!,
validator: (value) => Validators.phone(value, "phNumber"),
controller: controllers['insurerName']!,
// validator: (value) => Validators.phone(value, "phNumber"),
txtwidth: ResponsiveLayout.isMobile(context)
? null
: MediaQuery.of(context).size.width * 0.26,
@ -484,8 +740,8 @@ class PolicyTabState extends ConsumerState<PolicyTab> {
return buildResponsiveField(
label: "Policy Number",
field: ThemedFormField(
controller: controllers['mobile']!,
validator: (value) => Validators.phone(value, "phNumber"),
controller: controllers['policyNo']!,
// validator: (value) => Validators.phone(value, "phNumber"),
txtwidth: ResponsiveLayout.isMobile(context)
? null
: MediaQuery.of(context).size.width * 0.26,
@ -497,8 +753,7 @@ class PolicyTabState extends ConsumerState<PolicyTab> {
return buildResponsiveField(
label: "Payment Mode",
field: ThemedFormField(
controller: controllers['mobile']!,
validator: (value) => Validators.phone(value, "phNumber"),
controller: controllers['paymentMode']!,
txtwidth: ResponsiveLayout.isMobile(context)
? null
: MediaQuery.of(context).size.width * 0.26,
@ -510,7 +765,7 @@ class PolicyTabState extends ConsumerState<PolicyTab> {
return buildResponsiveField(
label: "Plan Type",
field: ThemedFormField(
controller: controllers['mobile']!,
controller: controllers['planType']!,
validator: (value) => Validators.phone(value, "phNumber"),
txtwidth: ResponsiveLayout.isMobile(context)
? null
@ -523,7 +778,7 @@ class PolicyTabState extends ConsumerState<PolicyTab> {
return buildResponsiveField(
label: "Premium Amount",
field: ThemedFormField(
controller: controllers['mobile']!,
controller: controllers['premAmount']!,
validator: (value) => Validators.phone(value, "phNumber"),
txtwidth: ResponsiveLayout.isMobile(context)
? null
@ -532,14 +787,6 @@ class PolicyTabState extends ConsumerState<PolicyTab> {
);
}
Widget buildClaimTable() {
return Text('data');
}
Widget buildEndrosmentTable() {
return Text('data');
}
static const _headerStyle = TextStyle(
color: Colors.black,
fontWeight: FontWeight.bold,
@ -549,4 +796,29 @@ class PolicyTabState extends ConsumerState<PolicyTab> {
fontSize: 14,
fontWeight: FontWeight.w600,
);
static final _dataBold = TextStyle(
fontSize: 14,
fontWeight: FontWeight.w400,
color: Color(0xFF000000),
);
static final _dataSub = TextStyle(
fontSize: 10,
fontWeight: FontWeight.w300,
color: Color(0xFF585757),
);
static const _cardheaderStyle = TextStyle(
color: Colors.black,
fontWeight: FontWeight.w600,
fontSize: 12,
);
static const _cardBodyStyle = TextStyle(
color: Color(0xFF545454),
fontWeight: FontWeight.w400,
fontSize: 12,
);
}

View File

@ -45,6 +45,7 @@ class QuotationTabState extends ConsumerState<QuotationTab> {
'regNum',
'insurer',
'idv',
'insuranceId',
'premAmount',
'planType',
'addit_doc',
@ -64,6 +65,7 @@ class QuotationTabState extends ConsumerState<QuotationTab> {
String? selectedId;
bool isLoading = false;
bool isquotation = false;
bool showAction = false;
Map<String, TextEditingController> controllers = {};
String? _token;
@ -78,13 +80,12 @@ class QuotationTabState extends ConsumerState<QuotationTab> {
Map<String, dynamic> 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,
"id": selectedId,
"enquiry_id": widget.id,
"insured_declared_value": controllers["idv"]?.text,
"premium_amount": controllers["premAmount"]?.text,
"insurance_plan_type_id": controllers["insuranceId"]?.text,
"updated_by": userId,
};
return data;
}
@ -129,42 +130,42 @@ class QuotationTabState extends ConsumerState<QuotationTab> {
});
try {
// if (widget.data != null) {
// final data = List<Map<String, dynamic>>.from(widget.data as Iterable);
//
// print('quoationListData - ${widget.data}');
// setState(() {
// getQuotationData = data;
// // getQuotationData = List<Map<String, dynamic>>.from(response['data']);
// originalData = getQuotationData;
// filteredData = List.from(originalData);
// // print('originalData - $getClaimPolicies');
// });
// }
final response;
final response = await apiService.findSingleQuotationData(managerId);
if (widget.data != null && widget.id != null) {
final data = List<Map<String, dynamic>>.from(widget.data as Iterable);
response = await apiService.findSingleQuotationData(widget.id);
// print('quoationListData - ${widget.data}');
// setState(() {
// getQuotationData = data;
// // getQuotationData = List<Map<String, dynamic>>.from(response['data']);
// originalData = getQuotationData;
// filteredData = List.from(originalData);
// // print('originalData - $getClaimPolicies');
// });
if (response['status'] == 'success') {
final rawData = response['data'];
print('quoationListData1 - ${response['data']}');
setState(() {
if (rawData is List) {
// already a list
getQuotationData = List<Map<String, dynamic>>.from(rawData);
} else if (rawData is Map) {
// single object, wrap into a list
getQuotationData = [Map<String, dynamic>.from(rawData)];
} else {
getQuotationData = [];
}
// getQuotationData = List<Map<String, dynamic>>.from(response['data']);
originalData = getQuotationData;
filteredData = List.from(originalData);
// print('originalData - $getClaimPolicies');
});
} else {
getQuotationData = [];
originalData = [];
if (response['status'] == 'success') {
final rawData = response['data'];
print('quoationListData1 - ${response['data']}');
setState(() {
if (rawData is List) {
// already a list
getQuotationData = List<Map<String, dynamic>>.from(rawData);
} else if (rawData is Map) {
// single object, wrap into a list
getQuotationData = [Map<String, dynamic>.from(rawData)];
} else {
getQuotationData = [];
}
// getQuotationData = List<Map<String, dynamic>>.from(response['data']);
originalData = getQuotationData;
filteredData = List.from(originalData);
// print('originalData - $getClaimPolicies');
});
} else {
getQuotationData = [];
originalData = [];
}
}
} catch (e) {
print('Exception occurred: $e');
@ -203,11 +204,33 @@ class QuotationTabState extends ConsumerState<QuotationTab> {
data['insured_declared_value']?.toString() ?? '';
controllers['premAmount']?.text =
data['premium_amount']?.toString() ?? '';
controllers['planType']?.text =
controllers['insuranceId']?.text =
data['insurance_plan_type_id']?.toString() ?? '';
controllers['addit_doc']?.text =
data['additional_uploaded_file_name']?.toString() ?? '';
controllers['planType']?.text =
data['insurance_plan_type']?.toString() ?? '';
controllers['remarks']?.text = data['reject_reason']?.toString() ?? '';
String? status = data['status']?.toString() ?? '';
if (status == 'Pending') {
print('status - $status');
showAction = true;
}
String? apiDocPath = data["additional_uploaded_file_name"];
if (apiDocPath != null && apiDocPath.isNotEmpty) {
print('apiDocPath - $apiDocPath');
selectedFileNames = apiDocPath.split('/').last;
print('selectedFileNames - $selectedFileNames');
docUploadedFileFromApi = apiDocPath;
print('docUploadedFileFromApi - $docUploadedFileFromApi');
docUploadedFile = null;
} else {
selectedFileNames = null;
docUploadedFile = null;
docUploadedFileFromApi = null;
}
});
}
@ -266,101 +289,205 @@ class QuotationTabState extends ConsumerState<QuotationTab> {
}
}
void UpdataDocuments() {
final dataSet = dataDetails();
print('UpdataDocuments - $dataSet}');
createDocumentData(dataSet);
}
Future<void> createDocumentData(Map<String, dynamic> userData) async {
final id = widget.id;
final uri = Uri.parse('${Env.apiUrl}quotation/updateQuotation');
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'] =
'nhance-partner-2025-signature-35468846JRhH551HK';
print("USerDAta - $userData");
// userData.forEach((key, value) {
// request.fields[key] = value.toString();
// print("✅ Encoded travel_details2: ${request.fields[key]}");
// });
userData.forEach((key, value) {
if (key != 'additional_uploaded_file_name') {
request.fields[key] = value.toString();
print("✅ Encoded $key: ${request.fields[key]}");
}
});
// Attach file if selected
if (docUploadedFile != null) {
try {
if (docUploadedFile!.bytes != null) {
final multipartFile = http.MultipartFile.fromBytes(
'additional_uploaded_file_name',
docUploadedFile!.bytes!,
filename: docUploadedFile!.name,
);
request.files.add(multipartFile);
} else if (docUploadedFile!.path != null) {
final multipartFile = await http.MultipartFile.fromPath(
'additional_uploaded_file_name',
docUploadedFile!.path!,
filename: docUploadedFile!.name,
);
request.files.add(multipartFile);
}
print("📎 File attached: ${docUploadedFile!.name}");
} catch (e) {
print("❌ Failed to attach file: $e");
}
}
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();
ToastHelper.showSuccessToast(context, 'Document Uploaded');
print("Response: ${response.body}");
// context.go(AppRoutes.agentLst);
} else {
final responseBody = jsonDecode(response.body);
dynamic msg = responseBody['data'];
print("❌ Submission failed. Status: ${response.statusCode}");
print("Body: ${response.body}");
ToastHelper.showErrorToast(context, 'Document Upload Failed');
}
} catch (e) {
print("🔥 Error submitting user: $e");
}
}
Color _getStatusColor(String? status) {
switch (status) {
case 'Accepted':
return Colors.green;
case 'Rejected':
return Colors.red;
default:
return Colors.grey;
}
}
@override
Widget build(BuildContext context) {
bool isMobile = ResponsiveLayout.isMobile(context);
return Container(
height: MediaQuery.of(context).size.height,
width: MediaQuery.of(context).size.width,
child: SingleChildScrollView(
scrollDirection: Axis.vertical,
child: Column(
children: [
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: filteredData.isNotEmpty
? SingleChildScrollView(
scrollDirection: Axis.vertical,
child: Column(
children: [
// Text('datta')
_buildDataTable(context),
],
),
),
const SizedBox(height: 20),
if (isquotation)
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),
Row(
mainAxisAlignment: MainAxisAlignment.end,
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: isMobile
? EdgeInsets.all(6.0)
: EdgeInsets.all(16.0),
child: Column(
children: [
GestureDetector(
onTap: () {
handleActions('Rejected');
},
child: Container(
padding: EdgeInsets.symmetric(
horizontal: 45.0,
vertical: 8,
),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8.0),
color: Color(0xFF425B5B),
),
child: Text(
'Reject',
style: TextStyle(color: Colors.white),
),
),
),
const SizedBox(width: 10),
GestureDetector(
onTap: () {
handleActions('Accepted');
},
child: Container(
padding: EdgeInsets.symmetric(
horizontal: 45.0,
vertical: 8,
),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8.0),
color: Color(0xFF425B5B),
),
child: Text(
'Accept',
style: TextStyle(color: Colors.white),
),
),
),
// Text('datta')
_buildDataTable(context),
],
),
],
),
),
const SizedBox(height: 20),
if (isquotation)
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: isMobile
? EdgeInsets.only(left: 14.0, right: 14.0)
: EdgeInsets.all(26.0),
child: Column(
children: [
SingleChildScrollView(
child: buildFormFields(context),
),
const SizedBox(height: 20),
if (showAction)
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
GestureDetector(
onTap: () {
handleActions('Rejected');
},
child: Container(
padding: EdgeInsets.symmetric(
horizontal: 45.0,
vertical: 8,
),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8.0),
color: Color(0xFF425B5B),
),
child: Text(
'Reject',
style: TextStyle(color: Colors.white),
),
),
),
const SizedBox(width: 10),
GestureDetector(
onTap: () {
handleActions('Accepted');
},
child: Container(
padding: EdgeInsets.symmetric(
horizontal: 45.0,
vertical: 8,
),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8.0),
color: Color(0xFF425B5B),
),
child: Text(
'Accept',
style: TextStyle(color: Colors.white),
),
),
),
],
),
],
),
),
],
),
],
),
),
)
: Center(child: Text('No Available Data')),
);
}
@ -531,12 +658,14 @@ class QuotationTabState extends ConsumerState<QuotationTab> {
Widget buildDocuments(BuildContext context) {
return buildResponsiveUploadField(
label: "Passport File",
label: 'Quotation Documents',
hintText: selectedFileNames,
onFileSelected: (fileName, file) {
setState(() {
docUploadedFile = file;
selectedFileNames = fileName;
UpdataDocuments();
});
},
showDownload: docUploadedFile != null || docUploadedFileFromApi != null,
@ -548,8 +677,8 @@ class QuotationTabState extends ConsumerState<QuotationTab> {
});
},
onDownload: () => apiService.downloadFile(
apiUrl: 'api/agent/downloadAgentCertificateFile?agent_id=$selectedId',
apiId: null,
apiUrl: 'api/quotation/downloadAdditionalUploadedFile?id=$selectedId',
apiId: selectedId,
localFile: docUploadedFile,
fileName: selectedFileNames,
),
@ -664,14 +793,14 @@ class QuotationTabState extends ConsumerState<QuotationTab> {
padding: const EdgeInsets.only(top: 5),
child: Row(
children: [
IconButton(
onPressed: onRemove,
icon: const Icon(
Icons.remove_circle_outline_rounded,
color: Colors.redAccent,
),
tooltip: 'Remove Upload',
),
// IconButton(
// onPressed: onRemove,
// icon: const Icon(
// Icons.remove_circle_outline_rounded,
// color: Colors.redAccent,
// ),
// tooltip: 'Remove Upload',
// ),
GestureDetector(
onTap: onDownload,
child: Container(
@ -788,6 +917,7 @@ class QuotationTabState extends ConsumerState<QuotationTab> {
Expanded(
flex: 1,
child: Checkbox(
activeColor: Colors.black,
value: selectedId == item['id'], // only selected row is checked
onChanged: (val) {
setState(() {
@ -823,10 +953,7 @@ class QuotationTabState extends ConsumerState<QuotationTab> {
),
Expanded(
flex: 1,
child: Text(
item['insurance_plan_type_id'] ?? '-',
style: _dataBold,
),
child: Text(item['insurance_plan_type'] ?? '-', style: _dataBold),
),
Expanded(
flex: 1,
@ -851,10 +978,11 @@ class QuotationTabState extends ConsumerState<QuotationTab> {
Widget _buildDataCard(Map<String, dynamic> item, int sno) {
return Container(
margin: const EdgeInsets.symmetric(vertical: 6, horizontal: 8),
// margin: const EdgeInsets.symmetric(vertical: 6, horizontal: 8),
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: const Color(0xFFF6FEFD),
color: const Color(0xFFFFFFFF),
// color: const Color(0xFFF6FEFD),
borderRadius: BorderRadius.circular(8.0),
border: Border.all(color: const Color(0xffD9EBE8)),
),
@ -864,6 +992,7 @@ class QuotationTabState extends ConsumerState<QuotationTab> {
children: [
// Single-select checkbox
Checkbox(
activeColor: Colors.black,
value: selectedId == item['id'], // only selected row is checked
onChanged: (val) {
setState(() {
@ -886,56 +1015,69 @@ class QuotationTabState extends ConsumerState<QuotationTab> {
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Company + Vehicle Reg No
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
// flex: 2,
child: Text(
item['insurer_name'] ?? '-',
style: _cardRow1BodyStyle,
),
),
mainAxisAlignment: MainAxisAlignment.start,
Expanded(
// flex: 2,
child: Text(
item['insurance_plan_type'] ?? '-',
style: _cardRow1BodyStyle,
),
),
],
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
// "TN64V3456",
item['reg_no'] ?? '-',
style: _cardheaderStyle,
),
Text(
'IDV: ${item['insured_declared_value']}' ?? '-',
style: _cardRow2BodyStyle,
),
],
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
RichText(
text: TextSpan(
children: [
Text(
item['insurer_name'] ?? '-',
style: _cardBodyStyle,
TextSpan(
text: 'Status : ', // key
style: TextStyle(
color: Colors.black,
fontWeight: FontWeight.w600,
fontSize: 11,
), // key color
),
Text(
// "TN64V3456",
item['reg_no'] ?? '-',
style: _cardheaderStyle,
),
Text(
'Status : ${item['status']}' ?? '-',
style: _cardBodyStyle,
TextSpan(
text: '${item['status'] ?? '-'}', // value
style: TextStyle(
color: _getStatusColor(item['status']),
fontWeight: FontWeight.w600,
fontSize: 11,
), // value color
),
],
),
),
SizedBox(width: 5),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: [
Text(
item['insurance_plan_type_id'] ?? '-',
style: _cardBodyStyle,
),
Text(
item['insured_declared_value'] ?? '-',
style: _cardBodyStyle,
),
Text(
'Premium Amount - ${item['premium_amount']}' ?? '-',
style: _cardheaderStyle,
),
],
),
Text(
'${item['premium_amount']}' ?? '-',
style: _cardheaderStyle,
),
],
),
@ -977,8 +1119,21 @@ class QuotationTabState extends ConsumerState<QuotationTab> {
);
static const _cardBodyStyle = TextStyle(
// color: Color(0xFF545454),
color: Colors.black,
fontWeight: FontWeight.w600,
fontSize: 12,
);
static const _cardRow1BodyStyle = TextStyle(
// color: Color(0xFF545454),
color: Colors.black,
fontWeight: FontWeight.w500,
fontSize: 12,
);
static const _cardRow2BodyStyle = TextStyle(
color: Color(0xFF545454),
fontWeight: FontWeight.w400,
// color: Colors.black,
fontWeight: FontWeight.w500,
fontSize: 12,
);
}

View File

@ -19,12 +19,13 @@ class TabEnquiryList extends ConsumerStatefulWidget {
}
class TabEnquiryListState extends ConsumerState<TabEnquiryList> {
int? expandedIndex;
int selectedIndex = 0;
List<TabItem> tabs = [];
Map<String, dynamic>? enquiryData;
bool isLoading = true;
late ApiService apiService;
ScrollController _scrollController = ScrollController();
@override
void initState() {
super.initState();
@ -76,6 +77,7 @@ class TabEnquiryListState extends ConsumerState<TabEnquiryList> {
data: (enquiryData?["policies"] as List<dynamic>?)!
.map((e) => Map<String, dynamic>.from(e as Map))
.toList(),
id: id ?? "",
),
),
@ -103,6 +105,7 @@ class TabEnquiryListState extends ConsumerState<TabEnquiryList> {
return MainLayout(
title: "Enquiry",
body: SafeArea(
child: isLoading
? const Center(child: CircularProgressIndicator())
@ -118,22 +121,55 @@ class TabEnquiryListState extends ConsumerState<TabEnquiryList> {
itemCount: tabs.length,
padding: const EdgeInsets.all(16),
itemBuilder: (context, index) {
final isExpanded = expandedIndex == index;
return Card(
margin: const EdgeInsets.only(bottom: 12),
color: const Color(0xFF425B5B),
color: isExpanded ? const Color(0xFFEDF6F5) : const Color(0xFF425B5B),
// color: Color(0xFFEDF6F5),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
side: BorderSide(
color: isExpanded ? Color(0xFF425B5B) : Color(0xFF425B5B),
// color: isExpanded ? Color(0xFFEDF6F5) : Color(0xFFEDF6F5),
width: 1,
),
),
child: ExpansionTile(
collapsedIconColor: Colors.white,
iconColor: Colors.white,
collapsedIconColor: isExpanded ? Colors.black : Colors.white,
iconColor: isExpanded ? Colors.black : Colors.white,
initiallyExpanded: isExpanded,
onExpansionChanged: (expanded) {
setState(() {
if (expanded) {
expandedIndex = index;
// Scroll to make the expanded tile visible
WidgetsBinding.instance.addPostFrameCallback((_) {
RenderBox box = context.findRenderObject() as RenderBox;
double yPos = box.localToGlobal(Offset.zero).dy;
_scrollController.animateTo(
_scrollController.offset +
yPos -
100, // adjust 100 if needed
duration: const Duration(milliseconds: 300),
curve: Curves.easeInOut,
);
});
} else {
expandedIndex = null;
}
});
},
title: Text(
tabs[index].title,
style: GoogleFonts.inter(
color: Colors.white,
color: isExpanded ? Colors.black : Colors.white,
fontWeight: FontWeight.w600,
),
),
children: [
Container(
padding: const EdgeInsets.all(12),
// padding: const EdgeInsets.all(12),
child: tabs[index].widget,
),
],
@ -150,7 +186,9 @@ class TabEnquiryListState extends ConsumerState<TabEnquiryList> {
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2),
child: GestureDetector(
onTap: () {
context.go(AppRoutes.dashboard);
// context.go(AppRoutes.dashboard);
ref.read(enquiryIdProvider.notifier).state = null;
context.go(AppRoutes.enquiryLst);
},
child: Row(
children: [
@ -213,424 +251,3 @@ class TabItem {
final Widget widget;
TabItem(this.title, this.widget);
}
// class TabEnquiryListState extends ConsumerState<TabEnquiryList> {
// int selectedIndex = 0; // 0 = Enquiry, 1 = Quotation, 2 = Policy
//
// // final List<String> tabs = ["Enquiry", "Quotation", "Policy"];
// // final List<Widget> tabWidgets = [EnquiryTab(), QuotationTab(), PolicyTab()];
// // late List<Widget> tabWidgets;
//
// late List<Map<String, dynamic>> tabs;
//
// Map<String, dynamic>? enquiryData;
// bool isLoading = true;
// late ApiService apiService;
//
// @override
// void initState() {
// super.initState();
// apiService = ApiService();
// _loadData();
// }
//
// Future<void> _loadData() async {
// final id = ref.read(enquiryIdProvider);
// if (id != null && id.isNotEmpty) {
// setState(() => isLoading = true);
// final response = await apiService.findEnqQuotePolicyView(id);
// setState(() {
// enquiryData = response["data"];
// isLoading = false;
// tabs = [
// TabItem("Enquiry", EnquiryTab(data: enquiryData?["enquiry"])),
// TabItem("Quotation", QuotationTab(id: id)),
// TabItem("Policy", PolicyTab()),
// ];
// });
// } else {
// setState(() {
// isLoading = false;
// tabs = [
// TabItem("Enquiry", EnquiryTab()),
// TabItem("Quotation", QuotationTab(id: "")),
// TabItem("Policy", PolicyTab()),
// ];
// });
// }
// }
//
// // Future<void> _fetchData() async {
// // final id = ref.read(enquiryIdProvider);
// // if (id != null && id.isNotEmpty) {
// // setState(() {
// // isLoading = true;
// // });
// //
// // final response = await apiService.findEnqQuotePolicyView(id);
// //
// // setState(() {
// // enquiryData = response["data"];
// // tabs = [
// // TabItem("Enquiry", EnquiryTab(data: enquiryData?["enquiry"])),
// // TabItem("Quotation", QuotationTab(id: id)),
// // TabItem("Policy", PolicyTab()),
// // ];
// // isLoading = false;
// // });
// // } else {
// // setState(() {
// // tabs = [
// // TabItem("Enquiry", EnquiryTab()),
// // TabItem("Quotation", QuotationTab(id: "")),
// // TabItem("Policy", PolicyTab()),
// // ];
// // isLoading = false;
// // });
// // }
// // }
//
// // Future<void> _fetchData() async {
// // final id = ref.read(enquiryIdProvider);
// // if (id != null && id.isNotEmpty) {
// // final response = await apiService.findEnqQuotePolicyView(id);
// // setState(() {
// // enquiryData = response["data"];
// // isLoading = false;
// // });
// // }
// // }
//
// // @override
// // Widget build(BuildContext context) {
// // final bool isMobile = MediaQuery.of(context).size.width < 600;
// // final id = ref.watch(enquiryIdProvider); // listen to provider
// //
// // print('ENQID: $id');
// // // final tabs = [
// // // TabItem("Enquiry", EnquiryTab(id: id ?? "")),
// // // TabItem("Quotation", QuotationTab(id: id ?? "")),
// // // TabItem("Policy", PolicyTab()),
// // // ];
// // final tabs;
// // if (id != null) {
// // print('HASENQID: $id');
// // setState(() {
// // isLoading = true;
// // });
// //
// // _fetchData();
// //
// // setState(() {
// // isLoading = false;
// // });
// //
// // tabs = [
// // TabItem("Enquiry", EnquiryTab(data: enquiryData?["enquiry"])),
// //
// // // TabItem("Quotation", QuotationTab(data: enquiryData?["quotations"])),
// // // TabItem("Policy", PolicyTab(data: enquiryData?["policies"])),
// // TabItem("Quotation", QuotationTab(id: id ?? "")),
// // TabItem("Policy", PolicyTab()),
// // ];
// // } else {
// // setState(() {
// // isLoading = false;
// // });
// //
// // print('HASENQIDNot');
// // tabs = [
// // TabItem("Enquiry", EnquiryTab()),
// //
// // // TabItem("Quotation", QuotationTab(data: enquiryData?["quotations"])),
// // // TabItem("Policy", PolicyTab(data: enquiryData?["policies"])),
// // TabItem("Quotation", QuotationTab(id: id ?? "")),
// // TabItem("Policy", PolicyTab()),
// // ];
// // }
// //
// // return MainLayout(
// // title: "Enquiry",
// // body: SafeArea(
// // child: isLoading
// // ? const Center(
// // child: CircularProgressIndicator(), // 👈 loader inside SafeArea
// // )
// // : isMobile
// // ? ListView.builder(
// // itemCount: tabs.length,
// // padding: const EdgeInsets.all(16),
// // itemBuilder: (context, index) {
// // return Card(
// // shape: RoundedRectangleBorder(
// // borderRadius: BorderRadius.circular(8),
// // ),
// // margin: const EdgeInsets.only(bottom: 12),
// // color: const Color(0xFF425B5B), // Header background
// // child: Theme(
// // data: Theme.of(context).copyWith(
// // dividerColor:
// // Colors.transparent, // remove default divider
// // ),
// // child: ExpansionTile(
// // collapsedIconColor: Colors.white,
// // iconColor: Colors.white,
// // title: Text(
// // // tabs[index]["title"],
// // tabs[index].title,
// // style: GoogleFonts.inter(
// // color: Colors.white,
// // fontWeight: FontWeight.w600,
// // ),
// // ),
// // children: [
// // Container(
// // width: double.infinity,
// // decoration: BoxDecoration(
// // color: const Color(0xFFEAF6F4),
// // borderRadius: BorderRadius.circular(8),
// // border: Border.all(
// // color: const Color(0xFFB0CFCB),
// // width: 1,
// // ),
// // ),
// // padding: const EdgeInsets.all(12),
// // child: SizedBox(
// // height: MediaQuery.of(context).size.height,
// // child: tabs[selectedIndex].widget,
// // // child: tabs[index]["widget"] as Widget,
// // ), // directly, no SizedBox height
// // ),
// // // Container(
// // // width: double.infinity,
// // //
// // // decoration: BoxDecoration(
// // // color: const Color(0xFFEAF6F4), // light teal bg like screenshot
// // // borderRadius: BorderRadius.circular(8),
// // // border: Border.all(
// // // color: const Color(0xFFB0CFCB), // soft border color
// // // width: 1,
// // // ),
// // // ),
// // // padding: const EdgeInsets.all(12),
// // // child: SizedBox(
// // // height: MediaQuery.of(context).size.height,
// // // // mainAxisSize: MainAxisSize.min, // 👈 prevents collapse
// // // // children: [
// // // child: tabs[index]["widget"] as Widget, // actual tab widget
// // // // ],
// // // ),
// // // ),
// // ],
// // ),
// // ),
// // );
// // },
// // )
// // : Column(
// // crossAxisAlignment: CrossAxisAlignment.start,
// // children: [
// // // Back Button + Title
// // Padding(
// // padding: const EdgeInsets.symmetric(
// // horizontal: 6,
// // vertical: 2,
// // ),
// // child: GestureDetector(
// // onTap: () {
// // context.go(AppRoutes.dashboard);
// // },
// // child: Row(
// // children: [
// // const Icon(
// // Icons.arrow_left_sharp,
// // size: 35,
// // color: Color(0xFF425B5B),
// // ),
// // const SizedBox(width: 8),
// // Text(
// // "Enquiry",
// // style: GoogleFonts.inter(
// // fontSize: 18,
// // fontWeight: FontWeight.w700,
// // color: Colors.black87,
// // ),
// // ),
// // ],
// // ),
// // ),
// // ),
// //
// // const SizedBox(height: 10),
// //
// // // Tabs
// // Padding(
// // padding: const EdgeInsets.symmetric(horizontal: 16),
// // child: Row(
// // children: List.generate(tabs.length, (index) {
// // final bool isSelected = selectedIndex == index;
// // return Padding(
// // padding: const EdgeInsets.only(right: 12),
// // child: ElevatedButton(
// // style: ElevatedButton.styleFrom(
// // elevation: 2,
// // backgroundColor: isSelected
// // ? const Color(0xFF425B5B)
// // : const Color(0xFFEDFFFC),
// // foregroundColor: isSelected
// // ? Colors.white
// // : Colors.black87,
// // shape: RoundedRectangleBorder(
// // borderRadius: BorderRadius.circular(6),
// // ),
// // padding: const EdgeInsets.symmetric(
// // horizontal: 24,
// // vertical: 12,
// // ),
// // ),
// // onPressed: () {
// // setState(() {
// // selectedIndex = index;
// // });
// // },
// // child: Text(
// // // tabs[index]["title"],
// // tabs[index].title,
// // style: GoogleFonts.inter(
// // fontWeight: isSelected
// // ? FontWeight.w600
// // : FontWeight.w500,
// // fontSize: 15,
// // ),
// // ),
// // ),
// // );
// // }),
// // ),
// // ),
// //
// // const SizedBox(height: 16),
// //
// // // Tab Content
// // Expanded(
// // child: Padding(
// // padding: const EdgeInsets.symmetric(horizontal: 16),
// // child: tabs[selectedIndex].widget,
// // // child: tabs[selectedIndex]["widget"],
// // ),
// // ),
// // ],
// // ),
// // ),
// // );
// // }
//
// // Widget build(BuildContext context) {
// // return MainLayout(
// // title: "Enquiry",
// // body: SafeArea(
// // child: Column(
// // crossAxisAlignment: CrossAxisAlignment.start,
// // mainAxisAlignment: MainAxisAlignment.start,
// // children: [
// // // Header
// // Padding(
// // padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2),
// // child: GestureDetector(
// // onTap: () {
// // context.go(AppRoutes.dashboard);
// // },
// // child: Row(
// // children: [
// // const Icon(
// // Icons.arrow_left_sharp,
// // size: 35,
// // color: Color(0xFF425B5B),
// // ),
// // const SizedBox(width: 8),
// // Text(
// // "Enquiry",
// // style: GoogleFonts.inter(
// // fontSize: 18,
// // fontWeight: FontWeight.w700,
// // color: Colors.black87,
// // ),
// // ),
// // ],
// // ),
// // ),
// // ),
// //
// // const SizedBox(height: 10),
// //
// // // Tab Buttons
// // Padding(
// // padding: const EdgeInsets.symmetric(horizontal: 16),
// // child: Row(
// // children: List.generate(tabs.length, (index) {
// // final bool isSelected = selectedIndex == index;
// // return Padding(
// // padding: const EdgeInsets.only(right: 12),
// // child: ElevatedButton(
// // style: ElevatedButton.styleFrom(
// // elevation: 2,
// // backgroundColor: isSelected
// // ? const Color(0xFF425B5B) // selected dark green
// // : const Color(0xFFEDFFFC), // light teal
// // foregroundColor: isSelected
// // ? Colors.white
// // : Colors.black87,
// // side: BorderSide(
// // color: isSelected
// // ? const Color(0xFF425B5B)
// // : const Color(0xFFEDFFFC),
// // ),
// // shape: RoundedRectangleBorder(
// // borderRadius: BorderRadius.circular(6),
// // ),
// // padding: const EdgeInsets.symmetric(
// // horizontal: 24,
// // vertical: 12,
// // ),
// // ),
// // onPressed: () {
// // setState(() {
// // selectedIndex = index;
// // });
// // },
// // child: Text(
// // tabs[index],
// // style: GoogleFonts.inter(
// // fontWeight: isSelected
// // ? FontWeight.w600
// // : FontWeight.w500,
// // fontSize: 15,
// // ),
// // ),
// // ),
// // );
// // }),
// // ),
// // ),
// //
// // const SizedBox(height: 16),
// //
// // // Tab content
// // Expanded(
// // child: Padding(
// // padding: const EdgeInsets.symmetric(horizontal: 16),
// // child: tabWidgets[selectedIndex],
// // ),
// // ),
// // ],
// // ),
// // ),
// // );
// // }
// }
// class TabItem {
// final String title;
// final Widget widget;
// TabItem(this.title, this.widget);
// }

View File

@ -249,6 +249,8 @@ class EnquiryListState extends ConsumerState<EnquiryList> {
SizedBox(width: 10),
GestureDetector(
onTap: () {
ref.read(enquiryIdProvider.notifier).state = null;
context.go(AppRoutes.tabEnquiry);
// print('Export');
},
child: Container(
@ -264,21 +266,33 @@ class EnquiryListState extends ConsumerState<EnquiryList> {
if (!ResponsiveLayout.isMobile(context)) ...[
SizedBox(width: 10),
GestureDetector(
onTap: () {
// context.go(AppRoutes.agent / create);
context.go('/tabEnquiry/tab');
},
child: Text(
'Create New Enquiry',
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.w600,
fontSize: 14,
),
Text(
'Create New Enquiry',
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.w600,
fontSize: 14,
),
),
// GestureDetector(
// onTap: () {
// ref
// .read(
// enquiryIdProvider.notifier,
// )
// .state =
// null;
// context.go(AppRoutes.tabEnquiry);
// },
// child: Text(
// 'Create New Enquiry',
// style: TextStyle(
// color: Colors.white,
// fontWeight: FontWeight.w600,
// fontSize: 14,
// ),
// ),
// ),
],
],
),
@ -288,51 +302,53 @@ class EnquiryListState extends ConsumerState<EnquiryList> {
),
),
SizedBox(height: 10),
Container(
decoration: BoxDecoration(
color: Color(0xFFEDF6F5),
borderRadius: BorderRadius.circular(6),
),
padding: const EdgeInsets.symmetric(
vertical: 12,
horizontal: 16,
),
child: const Row(
children: [
Expanded(
flex: 1,
child: Text('S.No.', style: _headerStyle),
),
Expanded(
flex: 2,
child: Text(
'Received Date & Time',
style: _headerStyle,
if (!ResponsiveLayout.isMobile(context))
Container(
decoration: BoxDecoration(
color: Color(0xFFEDF6F5),
borderRadius: BorderRadius.circular(6),
),
padding: const EdgeInsets.symmetric(
vertical: 12,
horizontal: 16,
),
child: const Row(
children: [
Expanded(
flex: 1,
child: Text('S.No.', style: _headerStyle),
),
),
Expanded(
flex: 1,
child: Text('Reg.No.', style: _headerStyle),
),
Expanded(
flex: 2,
child: Text('Company', style: _headerStyle),
),
Expanded(
flex: 2,
child: Text('Status', style: _headerStyle),
),
Expanded(
flex: 1,
child: Text('Remarks', style: _headerStyle),
),
Expanded(
flex: 1,
child: Text('Action', style: _headerStyle),
),
],
Expanded(
flex: 2,
child: Text(
'Received Date & Time',
style: _headerStyle,
),
),
Expanded(
flex: 1,
child: Text('Reg.No.', style: _headerStyle),
),
Expanded(
flex: 3,
child: Text('Company', style: _headerStyle),
),
Expanded(
flex: 2,
child: Text('Status', style: _headerStyle),
),
Expanded(
flex: 1,
child: Text('Remarks', style: _headerStyle),
),
Expanded(
flex: 1,
child: Text('Action', style: _headerStyle),
),
],
),
),
),
Expanded(child: _buildDataTable(context)),
],
),
@ -430,7 +446,7 @@ class EnquiryListState extends ConsumerState<EnquiryList> {
child: Text(item['reg_no'] ?? '-', style: _dataBold),
),
Expanded(
flex: 2,
flex: 3,
child: Text(
item['insurer_name'] ?? '-',
style: _dataBold,

View File

@ -0,0 +1,701 @@
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:intl/intl.dart';
import 'package:nhance_partner/presentation/providers/userRoleProvider.dart';
import '../../../../core/routing/routes.dart';
import '../../../../core/services/api_service.dart';
import '../../../../data/utils/Pagination.dart';
import '../../../layouts/main_layout.dart';
import '../../../layouts/responsive_layout.dart';
import '../../../providers/manager_provider.dart';
import '../../../themes/indicators/export_btn.dart';
import '../../../themes/indicators/search_field_theme.dart';
import '../../../widgets/custom_action_popup.dart';
// class claimList extends ConsumerStatefulWidget {
// const claimList({super.key});
// @override
// ConsumerState<claimList> createState() => claimListState();
// }
//
// class claimListState extends ConsumerState<claimList> {
// late ApiService apiService;
// List<Map<String, dynamic>> getClaimsHistoryList = [];
// List<String> stepKeys = [];
// late Map<String, dynamic> stepMap;
// int _index = 4;
// bool isLoading = false;
//
// @override
// void initState() {
// super.initState();
//
// apiService = ApiService();
// // print(widget.postToken);
// print("CLAIMHISTORY");
// print(widget.claimAmount);
// print(widget.claimNo);
// print(widget.clientPolicyNo);
// print(widget.empCode);
// print(widget.policyType);
// print(widget.ticket_id);
// getClaimsHistoryDetails();
// }
//
// @override
// void dispose() {
// super.dispose();
// }
//
// Future<void> getClaimsHistoryDetails() async {
// setState(() {
// isLoading = true;
// });
// try {
// print('10');
// // final ticketID = widget.ticket_id;
// // if (ticketID != '' || ticketID != null) {
// // return;
// // }
// final response = await apiService.getClaimsHistoryToApi(widget.ticket_id);
// if (response['status'] == 'success') {
// setState(() {
// isLoading = false;
// print('Claims History List1');
// // final List<dynamic> ticketDataList = response['data']['ticket_data'];
// // final Map<String, dynamic> ticketDataMap =
// // response['data']['ticket_data'];
// // print('Claims History List12');
// // print('Claims History List1- $ticketDataMap');
//
// // getClaimsHistoryList = ticketDataList
// // .map((item) => Map<String, dynamic>.from(item))
// // .toList();
//
// // getClaimsHistoryList = ticketDataMap.entries.map((entry) {
// // return {
// // 'status': entry.key,
// // ...Map<String, dynamic>.from(entry.value),
// // };
// // }).toList();
// //
// // getClaimsHistoryList = ticketDataMap.entries.map((entry) {
// // return {
// // 'status': entry.key,
// // ...Map<String, dynamic>.from(entry.value),
// // };
// // }).toList();
//
// // if (getClaimsHistoryList.isNotEmpty) {
// // stepMap = getClaimsHistoryList[0];
// // stepKeys = stepMap.keys.toList();
// // }
//
// print('Claims History List: $getClaimsHistoryList');
//
// getClaimsHistoryList = [
// Map<String, dynamic>.from(response['data']['ticket_data']),
// ];
// stepMap = getClaimsHistoryList[0];
// stepKeys = stepMap.keys.toList();
//
// // originalData = getCDPolicies;
// // filteredData = List.from(originalData);
// // print('filteredData');
// // print(filteredData);
// });
// } else {
// setState(() {
// isLoading = false;
// });
//
// // ToastHelper.showWarningToast(
// // context, 'Request failed with status: ${response.statusCode}');
// print('Request failed with status: ${response['code']}');
// }
// } catch (e) {
// setState(() {
// isLoading = false;
// });
// print('Exception occurred: $e');
// } finally {
// setState(() {
// // _isLoading = false;
// });
// }
// }
//
// @override
// Widget build(BuildContext context) {
// // if (getClaimsHistoryList.isEmpty) {
// // return SizedBox(
// // height: 50,
// // child: Center(child: Text('No available Claims')),
// // );
// // }
// final keyValueWidgets = [
// _buildKeyValue(
// 'Name',
// '${widget.empName ?? ''} (${widget.empCode ?? ''})',
// ),
// SizedBox(
// width: Responsive.isDesktop(context) ? 16 : 0,
// height: Responsive.isDesktop(context) ? 0 : 8,
// ),
// _buildKeyValue(
// 'Policy Name',
// '${widget.policyType ?? ''} - ${widget.clientPolicyNo ?? ''}',
// ),
// ];
//
// final keyValueWidgetRow = [
// _buildKeyValue(
// 'Claim Amount',
// widget.claimAmount == null || widget.claimAmount!.trim().isEmpty
// ? 'N/A'
// : '${widget.claimAmount}',
// ),
// SizedBox(
// width: Responsive.isDesktop(context) ? 16 : 0,
// height: Responsive.isDesktop(context) ? 0 : 8,
// ),
// _buildKeyValue(
// 'Claim Number',
// widget.claimNo == null || widget.claimNo!.trim().isEmpty
// ? 'N/A'
// : widget.claimNo!,
// ),
// ];
//
// return Container(
// constraints: BoxConstraints(maxWidth: 800, maxHeight: 800),
// padding: EdgeInsets.all(20),
// decoration: BoxDecoration(
// color: Colors.white,
// borderRadius: BorderRadius.circular(12),
// ),
// child: SingleChildScrollView(
// child: Column(
// crossAxisAlignment: CrossAxisAlignment.start,
// children: [
// Row(
// children: [
// Expanded(
// child: Align(
// alignment: Alignment.centerLeft,
// child: Text(
// 'Claim History',
// style: GoogleFonts.poppins(
// fontSize: Responsive.isDesktop(context) ? 20 : 16,
// fontWeight: FontWeight.w500,
// color: Color(0xFF101010),
// ),
// ),
// ),
// ),
// MouseRegion(
// cursor: SystemMouseCursors.click, // Show pointer cursor
// child: GestureDetector(
// onTap: () => Navigator.of(context).pop(),
// child: Container(
// height: 30,
// width: 30,
// decoration: BoxDecoration(
// color: Colors.white,
// border: Border.all(color: Color(0xFFBCBCBC)),
// borderRadius: BorderRadius.circular(6),
// ),
// child: Icon(
// Icons.close,
// size: 25,
// color: Color(0xFFBCBCBC),
// ),
// ),
// ),
// ),
// ],
// ),
// SizedBox(height: Responsive.isDesktop(context) ? 10 : 5),
// Container(
// margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 20),
// decoration: BoxDecoration(
// color: Color(0xFFFFFFFF), // White background
// borderRadius: BorderRadius.circular(12),
// boxShadow: [
// BoxShadow(
// color: Color(0xFFEBEBEB), // Shadow color
// blurRadius: 14, // How soft the shadow is
// spreadRadius: 2, // How much it spreads
// offset: Offset(0, 1), // X and Y offset
// ),
// ],
// ),
// padding: const EdgeInsets.all(16),
// child: Center(
// child: Column(
// crossAxisAlignment: CrossAxisAlignment.start,
// children: [
// Responsive.isDesktop(context)
// ? Row(
// children: keyValueWidgets
// .map((w) => Expanded(child: w))
// .toList(),
// )
// : Container(
// width: MediaQuery.of(context).size.width,
// child: Column(
// crossAxisAlignment: CrossAxisAlignment.start,
// children: keyValueWidgets,
// ),
// ),
// // Row(
// // children: [
// // Expanded(
// // child: _buildKeyValue('Name',
// // '${widget.empName ?? ''} (${widget.empCode ?? ''})'),
// // ),
// // SizedBox(width: 16),
// // Expanded(
// // child: _buildKeyValue('Policy Name',
// // '${widget.policyType ?? ''} - ${widget.clientPolicyNo ?? ''}')),
// // ],
// // ),
// SizedBox(height: Responsive.isDesktop(context) ? 16 : 8),
// Responsive.isDesktop(context)
// ? Row(
// children: keyValueWidgetRow
// .map((w) => Expanded(child: w))
// .toList(),
// )
// : Container(
// width: MediaQuery.of(context).size.width,
// child: Column(
// crossAxisAlignment: CrossAxisAlignment.start,
// children: keyValueWidgetRow,
// ),
// ),
// ],
// ),
// ),
// ),
// SizedBox(height: 10),
//
// // Container(
// // padding: const EdgeInsets.all(16),
// // child: Stepper(
// // currentStep: _index,
// // onStepCancel: () {
// // if (_index > 0) {
// // setState(() {
// // _index -= 1;
// // });
// // }
// // },
// // onStepContinue: () {
// // if (_index < 4) {
// // setState(() {
// // _index += 1;
// // });
// // }
// // },
// // onStepTapped: (int index) {
// // setState(() {
// // _index = index;
// // });
// // },
// // steps: <Step>[
// // Step(
// // title: Text('Step 1: PAYMENT INITIATED'),
// // content: Text('Pay Initiate Date: 05-05-2025'),
// // isActive: true,
// // state: StepState.complete,
// // ),
// // Step(
// // title: Text('Step 2: INFORMATION REQUIRED'),
// // content: Text('Raised Date: 05-05-2025'),
// // isActive: true,
// // state: StepState.complete,
// // ),
// // Step(
// // title: Text('Step 3: CLAIM NO. UPDATION'),
// // content: Column(
// // crossAxisAlignment: CrossAxisAlignment.start,
// // children: [
// // Text('Claim Number: 4315440'),
// // Text('Registration Date: 05-05-2025'),
// // ],
// // ),
// // isActive: true,
// // state: StepState.complete,
// // ),
// // Step(
// // title: Text('Step 4: QUERY DOCUMENT REQUIRED'),
// // content: Text('Query Received Date: 05-05-2025'),
// // isActive: true,
// // state: StepState.complete,
// // ),
// // Step(
// // title: Text('Step 5: APPROVED'),
// // content: Column(
// // crossAxisAlignment: CrossAxisAlignment.start,
// // children: [
// // Text('Approved Amount: 4315440'),
// // Text('Approved Date: 05-05-2025'),
// // Text('Approved Letter: Lorem ipsum...'),
// // Text('Description: Lorem ipsum...'),
// // ],
// // ),
// // isActive: true,
// // state: StepState.complete,
// // ),
// // ],
// // ),
// // )
// // Column(
// // crossAxisAlignment: CrossAxisAlignment.start,
// // children: List.generate(5, (index) {
// // return _buildStep(
// // stepNumber: index + 1,
// // title: _getStepTitle(index),
// // content: _getStepContent(index),
// // isLast: index == 4,
// // );
// // }),
// // )
// isLoading
// ? Container(
// // color: Color(0x98FFFCE5), // Semi-transparent background
// child: Center(
// child: // Your GIF loader widget
// Image.asset(
// height: 60,
// width: 60,
// 'assets/nhance-loader.gif',
// ), // Adjust path to your GIF loader
// ),
// )
// : Container(
// child: getClaimsHistoryList.isNotEmpty
// ? Column(
// crossAxisAlignment: CrossAxisAlignment.start,
// children: List.generate(stepKeys.length, (index) {
// String stepTitleKey = stepKeys[index];
// Map<String, dynamic> stepData =
// stepMap[stepTitleKey];
//
// print('stepTitleKey');
// print(stepTitleKey);
// print('stepData');
// print(stepData);
//
// Widget content = _getStepContentFromApi(stepData);
//
// return _buildStep(
// stepNumber: index + 1,
// title: _getStepTitleFromApi(
// stepTitleKey,
// stepData,
// ),
// content: content,
// // content: _getStepContentFromApi(stepData),
// isLast: index == stepKeys.length - 1,
// );
// }),
// )
// : Container(
// height: MediaQuery.of(context).size.height * 0.4,
// // color: Colors.red,
// child: Center(
// child: Column(
// children: [
// Image.asset(
// 'assets/searchData.jpg', // Replace 'default_image.png' with your default image asset path
// width: 200,
// height: 200,
// fit: BoxFit.cover,
// ),
// Text(
// 'No Claim History',
// style: TextStyle(
// fontWeight: FontWeight.w500,
// fontSize: 15,
// ),
// ),
// ],
// ),
// ),
// ),
// ),
// ],
// ),
// ),
// );
// }
//
// Widget _buildStep({
// required int stepNumber,
// // required String title,
// required Widget title,
// required Widget content,
// bool isLast = false,
// }) {
// print(title);
// final noContent;
// if ((content as Column).children.isEmpty) {
// noContent = 0;
// } else {
// noContent = 1;
// }
//
// return Row(
// crossAxisAlignment: CrossAxisAlignment.start,
// children: [
// // Left Column with circle + line
// Column(
// children: [
// // Add top spacing before circle
// SizedBox(height: stepNumber == 1 ? 0 : 4),
//
// // Step number circle
// Container(
// height: 28,
// width: 28,
// decoration: BoxDecoration(
// color: Color(0xFF00A5A8),
// shape: BoxShape.circle,
// ),
// alignment: Alignment.center,
// child: Text(
// '$stepNumber',
// style: TextStyle(
// color: Colors.white,
// fontSize: Responsive.isDesktop(context) ? 13 : 12,
// fontWeight: FontWeight.w600,
// ),
// ),
// ),
//
// // Dotted line below circle (except for last step)
// if (!isLast)
// Container(
// height: noContent == 1 ? 50 : 20,
// // increase to extend line
// width: 2,
// margin: EdgeInsets.only(top: 4, bottom: 4),
// child: CustomPaint(painter: DottedLinePainter()),
// ),
// ],
// ),
//
// SizedBox(width: 12),
//
// // Right Side: Step title and content
// Expanded(
// child: Column(
// crossAxisAlignment: CrossAxisAlignment.start,
// children: [
// title,
// if (noContent == 1) SizedBox(height: noContent == 0 ? 0 : 8),
// if (noContent == 1)
// Container(
// width: double.infinity,
// padding: EdgeInsets.all(12),
// decoration: BoxDecoration(
// color: Color(0xFFF7F7F7),
// borderRadius: BorderRadius.circular(8),
// border: Border.all(color: Color(0xFFE0E0E0)),
// ),
// child: content,
// ),
// if (noContent == 1) SizedBox(height: isLast ? 0 : 16),
// ],
// ),
// ),
// ],
// );
// }
//
// // String _getStepTitleFromApi(String status, Map<String, dynamic> data) {
// // final modifiedBy = data['modified_by'] ?? '';
// // final modifiedAt = data['modified_at'] ?? '';
// // return '$status ($modifiedBy $modifiedAt)';
// // }
//
// Widget _getStepTitleFromApi(String status, Map<String, dynamic> data) {
// final modifiedBy = data['modified_by'] ?? '';
// final modifiedAt = data['modified_at'] ?? '';
// final isDesktop = Responsive.isDesktop(context);
//
// return RichText(
// text: TextSpan(
// children: [
// TextSpan(
// text: status + (isDesktop ? ' ' : '\n'),
// style: GoogleFonts.poppins(
// fontSize: Responsive.isDesktop(context) ? 15 : 11,
// fontWeight: FontWeight.w500,
// color: Color(0xFF212120), // Status color
// ),
// ),
// TextSpan(
// text: ' ($modifiedBy $modifiedAt)',
// style: GoogleFonts.poppins(
// fontSize: Responsive.isDesktop(context) ? 14 : 11,
// fontWeight: FontWeight.w400,
// color: Color(0xFF565656), // Subtitle color
// ),
// ),
// ],
// ),
// );
// }
//
// Widget _getStepContentFromApi(Map<String, dynamic> data) {
// List<Widget> rows = [];
//
// data.forEach((key, value) {
// // Skip metadata fields
// if (key == 'modified_by' || key == 'modified_at') return;
//
// String displayName = value['display_name'] ?? key;
// String displayValue = value['display_value'] ?? 'N/A';
//
// rows.add(_buildHistoryListData(displayName, displayValue));
// rows.add(SizedBox(height: 1));
// });
//
// return Column(crossAxisAlignment: CrossAxisAlignment.start, children: rows);
// }
//
// Widget _buildKeyValue(String title, String value) {
// final displayValue = (value == null || value.trim().isEmpty)
// ? 'N/A'
// : value;
// return Column(
// crossAxisAlignment: CrossAxisAlignment.start,
// children: [
// Text(
// title,
// style: GoogleFonts.poppins(
// color: Color(0xFF747474),
// fontWeight: FontWeight.w400,
// fontSize: Responsive.isDesktop(context) ? 16 : 13,
// ),
// ),
// SizedBox(height: 4),
// Text(
// displayValue,
// style: GoogleFonts.poppins(
// color: Color(0xFF000000),
// fontWeight: FontWeight.w500,
// fontSize: Responsive.isDesktop(context) ? 16 : 13,
// ),
// ),
// ],
// );
// }
//
// Widget _buildHistoryListData(String title, String value) {
// final displayValue = (value == null || value.trim().isEmpty)
// ? 'N/A'
// : value;
//
// final titleText = Text(
// title,
// style: GoogleFonts.poppins(
// color: const Color(0xFF747474),
// fontWeight: FontWeight.w400,
// fontSize: Responsive.isDesktop(context) ? 14 : 12,
// ),
// );
//
// final valueText = Text(
// displayValue,
// style: GoogleFonts.poppins(
// color: const Color(0xFF000000),
// fontWeight: FontWeight.w400,
// fontSize: Responsive.isDesktop(context) ? 14 : 12,
// ),
// textAlign: TextAlign.right,
// );
//
// return Padding(
// padding: const EdgeInsets.symmetric(vertical: 1), // optional spacing
// child: Responsive.isDesktop(context)
// ? Row(
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
// crossAxisAlignment: CrossAxisAlignment.center,
// children: [
// Expanded(
// child: Align(
// alignment: Alignment.centerLeft,
// child: titleText,
// ),
// ),
// Expanded(
// child: Align(
// alignment: Alignment.centerRight,
// child: valueText,
// ),
// ),
//
// // Title - Align to center left
// // Expanded(
// // child: Align(
// // alignment: Alignment.centerLeft,
// // child: Text(
// // title,
// // style: GoogleFonts.poppins(
// // color: const Color(0xFF747474),
// // fontWeight: FontWeight.w400,
// // fontSize: 14,
// // ),
// // ),
// // ),
// // ),
//
// // Value - Align to center right
// // Expanded(
// // child: Align(
// // alignment: Alignment.centerRight,
// // child: Text(
// // displayValue,
// // style: GoogleFonts.poppins(
// // color: const Color(0xFF000000),
// // fontWeight: FontWeight.w400,
// // fontSize: 14,
// // ),
// // ),
// // ),
// // ),
// ],
// )
// : Column(
// crossAxisAlignment: CrossAxisAlignment.start,
// children: [titleText, SizedBox(height: 4), valueText],
// ),
// );
// }
// }
//
// class DottedLinePainter extends CustomPainter {
// @override
// void paint(Canvas canvas, Size size) {
// const dashHeight = 2.0;
// const dashSpace = 3.0;
// double startY = 0;
// final paint = Paint()
// ..color = Colors.grey.shade400
// ..strokeWidth = 1;
//
// while (startY < size.height) {
// canvas.drawLine(Offset(0, startY), Offset(0, startY + dashHeight), paint);
// startY += dashHeight + dashSpace;
// }
// }
//
// @override
// bool shouldRepaint(CustomPainter oldDelegate) => false;
// }

View File

@ -0,0 +1,431 @@
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:http/http.dart';
import 'package:intl/intl.dart';
import 'package:nhance_partner/presentation/providers/userRoleProvider.dart';
import '../../../../core/routing/routes.dart';
import '../../../../core/services/api_service.dart';
import '../../../../data/utils/Pagination.dart';
import '../../../layouts/responsive_layout.dart';
import '../../../providers/manager_provider.dart';
import '../../../widgets/custom_action_popup.dart';
class ClaimsSubList extends ConsumerStatefulWidget {
final dynamic policyNumber;
ClaimsSubList({super.key, this.policyNumber});
@override
ConsumerState<ClaimsSubList> createState() => ClaimsSubListState();
}
class ClaimsSubListState extends ConsumerState<ClaimsSubList> {
int currentPage = 1;
int itemsPerPage = 10;
late ApiService apiService;
// List<Map<String, dynamic>> dataVal = [];
List<Map<String, dynamic>> getStaffData = [];
List<Map<String, dynamic>> originalData = [];
List<Map<String, dynamic>> filteredData = [];
bool isLoading = false;
@override
void initState() {
super.initState();
apiService = ApiService();
if (widget.policyNumber != null) {
getClaimsList(widget.policyNumber);
}
// Future.microtask(() {
// // final id = ref.read(managerIdProvider);
// // final roleId = ref.read(userRoleProvider);
//
// // print("ENQroleId - $roleId");
//
// });
}
Future<void> getClaimsList(id) async {
// print('getClaimList called MagId - $managerId - $role');
setState(() {
isLoading = true;
});
// dynamic id = widget.policyNumber;
print('POLID - $id');
try {
final response = await apiService.findSingleClaimData(id);
if (response['status'] == 'success') {
final data = response['data'];
print('getClaimsListData - ${response['data']}');
setState(() {
if (data is List) {
// Already a list of maps
getStaffData = List<Map<String, dynamic>>.from(data);
} else if (data is Map) {
// Single object, wrap in a list
getStaffData = [Map<String, dynamic>.from(data)];
} else {
getStaffData = [];
}
// getStaffData = List<Map<String, dynamic>>.from(response['data']);
originalData = getStaffData;
filteredData = List.from(originalData);
// print('originalData - $getClaimPolicies');
});
} else {
getStaffData = [];
originalData = [];
}
} catch (e) {
print('Exception occurred: $e');
} finally {
setState(() {
isLoading = false;
});
}
}
final TextEditingController _searchStaffController = TextEditingController();
String _formatDate(String rawDate) {
try {
final dateTime = DateTime.parse(rawDate);
return DateFormat('dd/MM/yyyy HH:mm').format(dateTime); // 24-hour format
} catch (e) {
return rawDate; // fallback if parsing fails
}
}
List<Widget> _buildPopupMenuActions(BuildContext context, dynamic data) {
return [
GestureDetector(
onTap: () {
Navigator.pop(context);
print('EDITStaff - ${data['id']}');
// dynamic id = data['id'];
// context.go('/tabEnquiry/$id');
// update provider
final id = data['id'].toString();
ref.read(enquiryIdProvider.notifier).state = id;
context.go(AppRoutes.tabEnquiry);
},
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(Icons.edit_sharp, color: Color(0xFF319718), size: 18),
SizedBox(width: 10),
Text('Edit'),
],
),
),
];
}
@override
Widget build(BuildContext context) {
return Container(
// color: Colors.yellow.shade50,
width: MediaQuery.of(context).size.width * 0.6,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: [
Container(
// color: Colors.green,
// color: Colors.green.shade50,
width: MediaQuery.of(context).size.width,
padding: EdgeInsets.all(8.0),
child: Column(
children: [
if (!ResponsiveLayout.isMobile(context))
Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(6),
),
padding: const EdgeInsets.symmetric(
vertical: 12,
horizontal: 16,
),
child: const Row(
children: [
Expanded(
flex: 2,
child: Text('Claim Type', style: _headerStyle),
),
Expanded(
flex: 2,
child: Text('Claim Amount', style: _headerStyle),
),
Expanded(
flex: 3,
child: Text('Remarks', style: _headerStyle),
),
Expanded(
flex: 2,
child: Text('Status', style: _headerStyle),
),
Expanded(
flex: 1,
child: Text('Action', style: _headerStyle),
),
],
),
),
_buildDataTable(context),
],
),
),
],
),
);
}
Widget _buildDataTable1(BuildContext context) {
if (filteredData.isEmpty) {
return const SizedBox(
height: 50,
child: Center(child: Text('No available data')),
);
}
final sortedData = [...filteredData];
return ListView.builder(
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 _buildDataTable(BuildContext context) {
print('DATA -1');
if (filteredData.isEmpty) {
return const SizedBox(
height: 50,
child: Center(child: Text('No available data')),
);
}
print('DATA -2');
final sortedData = [...filteredData];
print('DATA -$sortedData');
return ListView.builder(
shrinkWrap: true, // 👈 important when inside Column
physics: const NeverScrollableScrollPhysics(), // let parent scroll
itemCount: sortedData.length,
itemBuilder: (context, index) {
print('DAta Itembuilder 1');
final startIndex = (currentPage - 1) * itemsPerPage;
final item = sortedData[index];
final sno = startIndex + index + 1;
print('DAta Itembuilder 2');
return !ResponsiveLayout.isMobile(context)
? _buildDataRow(item, sno)
: _buildDataCard(item, sno);
},
);
}
Widget _buildHeader() {
return SizedBox.shrink();
}
Widget _buildDataRow(Map<String, dynamic> item, sno) {
print('Build - $item'); // Debug: check data
return Container(
padding: const EdgeInsets.symmetric(vertical: 6, horizontal: 16),
decoration: BoxDecoration(
color: Color(0xFFEDF6F5),
border: const Border(
bottom: BorderSide(color: Color(0xFFEAEAEA), width: 1),
),
borderRadius: BorderRadius.circular(8),
),
child: Row(
children: [
Expanded(
flex: 2,
child: Text(item['claim_type_value'] ?? '-', style: _dataBold),
),
Expanded(
flex: 2,
child: Text(item['claim_amount'] ?? '-', style: _dataBold),
),
Expanded(
flex: 3,
child: Text(item['claim_description'] ?? '-', style: _dataBold),
),
Expanded(
flex: 2,
child: Text(item['claim_status_value'] ?? '-', style: _dataBold),
),
Expanded(
flex: 1,
child: Row(
children: [
PopupMenuButton<int>(
color: Colors.white,
padding: EdgeInsets.zero,
offset: Offset(0, 30),
icon: Icon(
Icons.more_vert,
color: Color(0xFF475569),
size: 14,
),
itemBuilder: (context) => [
CustomPopupMenuEntry(
child: Container(
padding: EdgeInsets.symmetric(
horizontal: 8,
vertical: 8,
),
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
children: _buildPopupMenuActions(context, item),
),
),
),
],
),
],
),
),
],
),
);
}
Widget _buildDataCard(Map<String, dynamic> item, int sno) {
return Container(
margin: const EdgeInsets.symmetric(vertical: 6, horizontal: 8),
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
// color: const Color(0xFFF6FEFD),
color: const Color(0xFFFFFFFF),
borderRadius: BorderRadius.circular(8.0),
border: Border.all(color: const Color(0xffD9EBE8)),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Title + menu
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: Text(
item['claim_type_value'] ?? '-',
style: _headerStyle,
),
),
PopupMenuButton<int>(
color: Colors.white,
padding: EdgeInsets.zero,
offset: const Offset(0, 30),
icon: const Icon(
Icons.more_vert,
color: Color(0xFF475569),
size: 16,
),
itemBuilder: (context) => [
PopupMenuItem<int>(
value: 1,
child: Column(
mainAxisSize: MainAxisSize.min,
children: _buildPopupMenuActions(context, item),
),
),
],
),
],
),
// Description
Text(
item['claim_description'] ?? '-',
style: _cardBodyStyle,
maxLines: 3,
overflow: TextOverflow.ellipsis,
),
// Status
Row(
children: [
const Text(
"Status: ",
style: TextStyle(
color: Colors.green,
fontWeight: FontWeight.w600,
fontSize: 12,
),
),
Text(
item['claim_status_value'] ?? '-',
style: TextStyle(
// color: (item['status'] == "Completed")
// ? Colors.green
// : Colors.orange,
fontWeight: FontWeight.w600,
),
),
],
),
],
),
);
}
static final _dataBold = TextStyle(
fontSize: 14,
fontWeight: FontWeight.w400,
color: Color(0xFF000000),
);
static final _dataSub = TextStyle(
fontSize: 10,
fontWeight: FontWeight.w300,
color: Color(0xFF585757),
);
static const _headerStyle = TextStyle(
color: Colors.black,
fontWeight: FontWeight.bold,
);
static const _cardheaderStyle = TextStyle(
color: Colors.black,
fontWeight: FontWeight.w600,
fontSize: 12,
);
static const _cardBodyStyle = TextStyle(
color: Color(0xFF545454),
fontWeight: FontWeight.w400,
fontSize: 12,
);
}

View File

@ -0,0 +1,464 @@
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:http/http.dart';
import 'package:intl/intl.dart';
import 'package:nhance_partner/presentation/providers/userRoleProvider.dart';
import '../../../../core/routing/routes.dart';
import '../../../../core/services/api_service.dart';
import '../../../../data/utils/Pagination.dart';
import '../../../layouts/responsive_layout.dart';
import '../../../providers/manager_provider.dart';
import '../../../widgets/custom_action_popup.dart';
class EndrosmentSubList extends ConsumerStatefulWidget {
final dynamic policyNumber;
const EndrosmentSubList({super.key, this.policyNumber});
@override
ConsumerState<EndrosmentSubList> createState() => EndrosmentSubListState();
}
class EndrosmentSubListState extends ConsumerState<EndrosmentSubList> {
int currentPage = 1;
int itemsPerPage = 10;
late ApiService apiService;
// List<Map<String, dynamic>> dataVal = [];
List<Map<String, dynamic>> getStaffData = [];
List<Map<String, dynamic>> originalData = [];
List<Map<String, dynamic>> filteredData = [];
bool isLoading = false;
@override
void initState() {
super.initState();
apiService = ApiService();
if (widget.policyNumber != null) {
getEndrosmentList(widget.policyNumber);
}
// Future.microtask(() {
// // final id = ref.read(managerIdProvider);
// // final roleId = ref.read(userRoleProvider);
//
// });
}
Future<void> getEndrosmentList(id) async {
print('POLID - $id');
// print('getClaimList called MagId - $managerId - $role');
setState(() {
isLoading = true;
});
// dynamic id = widget.policyNumber;
try {
final response = await apiService.findSingleEnrosmentData(id);
if (response['status'] == 'success') {
final data = response['data'];
print('getEndrosmentData - ${response['data']}');
setState(() {
if (data is List) {
print('getEndrosmentData 1');
// Already a list of maps
getStaffData = List<Map<String, dynamic>>.from(data);
} else if (data is Map) {
print('getEndrosmentData 2 ');
// Single object, wrap in a list
getStaffData = [Map<String, dynamic>.from(data)];
print('getEndrosmentData 2 - $getStaffData');
} else {
print('getEndrosmentData 3');
getStaffData = [];
}
// getStaffData = List<Map<String, dynamic>>.from(response['data']);
originalData = getStaffData;
filteredData = List.from(originalData);
// print('originalData - $getClaimPolicies');
});
} else {
getStaffData = [];
originalData = [];
}
} catch (e) {
print('Exception occurred: $e');
} finally {
setState(() {
isLoading = false;
});
}
}
final TextEditingController _searchStaffController = TextEditingController();
String _formatDate(String rawDate) {
try {
final dateTime = DateTime.parse(rawDate);
return DateFormat('dd/MM/yyyy HH:mm').format(dateTime); // 24-hour format
} catch (e) {
return rawDate; // fallback if parsing fails
}
}
List<dynamic> get _paginatedData {
// Sort descending by id first
final sortedData = [...filteredData]
..sort((a, b) => int.parse(b['id']) - int.parse(a['id']));
final startIndex = (currentPage - 1) * itemsPerPage;
final endIndex = (currentPage * itemsPerPage).clamp(0, sortedData.length);
return sortedData.sublist(startIndex, endIndex);
}
List<Widget> _buildPopupMenuActions(BuildContext context, dynamic data) {
return [
GestureDetector(
onTap: () {
Navigator.pop(context);
print('EDITStaff - ${data['id']}');
// dynamic id = data['id'];
// context.go('/tabEnquiry/$id');
// update provider
final id = data['id'].toString();
ref.read(enquiryIdProvider.notifier).state = id;
context.go(AppRoutes.tabEnquiry);
},
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(Icons.edit_sharp, color: Color(0xFF319718), size: 18),
SizedBox(width: 10),
Text('Edit'),
],
),
),
];
}
@override
Widget build(BuildContext context) {
return Container(
// color: Colors.yellow.shade50,
width: MediaQuery.of(context).size.width * 0.6,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: [
Container(
// color: Colors.green,
// color: Colors.green.shade50,
width: MediaQuery.of(context).size.width,
padding: EdgeInsets.all(8.0),
child: Column(
children: [
if (!ResponsiveLayout.isMobile(context))
Container(
decoration: BoxDecoration(
color: Colors.white,
// color: Color(0xFFEDF6F5),
borderRadius: BorderRadius.circular(6),
),
padding: const EdgeInsets.symmetric(
vertical: 12,
horizontal: 16,
),
child: const Row(
children: [
Expanded(
flex: 2,
child: Text('Endorsement Type', style: _headerStyle),
),
Expanded(
flex: 2,
child: Text(
'Endorsement Number',
style: _headerStyle,
),
),
Expanded(
flex: 3,
child: Text('Remarks', style: _headerStyle),
),
Expanded(
flex: 2,
child: Text('Status', style: _headerStyle),
),
Expanded(
flex: 1,
child: Text('Action', style: _headerStyle),
),
],
),
),
_buildDataTable(context),
],
),
),
],
),
);
}
Widget _buildDataTable1(BuildContext context) {
print('DATA -1');
if (filteredData.isEmpty) {
return const SizedBox(
height: 50,
child: Center(child: Text('No available data')),
);
}
print('DATA -2');
// final sortedData = [..._paginatedData];
final sortedData = [...filteredData];
print('DATA -$sortedData');
return ListView.builder(
itemCount: ResponsiveLayout.isMobile(context)
? sortedData
.length // only cards for mobile
: sortedData.length + 1, // +1 for header in desktop
// itemCount: sortedData.length,
itemBuilder: (context, index) {
print('DAta Itembuilder');
if (!ResponsiveLayout.isMobile(context) && index == 0) {
return _buildHeader();
}
print('DAta Itembuilder 1');
final startIndex = (currentPage - 1) * itemsPerPage;
print('DAta Itembuilder 2');
final item =
sortedData[index - (ResponsiveLayout.isMobile(context) ? 0 : 1)];
final sno = startIndex + index;
print('DAta Itembuilder 3');
// return _buildDataRow(item, sno);
return !ResponsiveLayout.isMobile(context)
? _buildDataRow(item, sno)
: _buildDataCard(item, sno);
},
);
}
Widget _buildDataTable(BuildContext context) {
print('DATA -1');
if (filteredData.isEmpty) {
return const SizedBox(
height: 50,
child: Center(child: Text('No available data')),
);
}
print('DATA -2');
final sortedData = [...filteredData];
print('DATA -$sortedData');
return ListView.builder(
shrinkWrap: true, // 👈 important when inside Column
physics: const NeverScrollableScrollPhysics(), // let parent scroll
itemCount: sortedData.length,
itemBuilder: (context, index) {
print('DAta Itembuilder 1');
final startIndex = (currentPage - 1) * itemsPerPage;
final item = sortedData[index];
final sno = startIndex + index + 1;
print('DAta Itembuilder 2');
return !ResponsiveLayout.isMobile(context)
? _buildDataRow(item, sno)
: _buildDataCard(item, sno);
},
);
}
Widget _buildHeader() {
return SizedBox.shrink();
}
Widget _buildDataRow(Map<String, dynamic> item, sno) {
print('Build - $item'); // Debug: check data
return Container(
padding: const EdgeInsets.symmetric(vertical: 6, horizontal: 16),
decoration: BoxDecoration(
color: Color(0xFFEDF6F5),
border: const Border(
bottom: BorderSide(color: Color(0xFFEAEAEA), width: 1),
),
borderRadius: BorderRadius.circular(8),
),
child: Row(
children: [
Expanded(
flex: 2,
child: Text(
item['endorsement_type_value'] ?? '-',
style: _dataBold,
),
),
Expanded(
flex: 2,
child: Text(item['endorsement_no'] ?? '-', style: _dataBold),
),
Expanded(
flex: 3,
child: Text(
item['endorsement_description'] ?? '-',
style: _dataBold,
),
),
Expanded(
flex: 2,
child: Text(item['status'] ?? '-', style: _dataBold),
),
Expanded(
flex: 1,
child: Row(
children: [
PopupMenuButton<int>(
color: Colors.white,
padding: EdgeInsets.zero,
offset: Offset(0, 30),
icon: Icon(
Icons.more_vert,
color: Color(0xFF475569),
size: 14,
),
itemBuilder: (context) => [
CustomPopupMenuEntry(
child: Container(
padding: EdgeInsets.symmetric(
horizontal: 8,
vertical: 8,
),
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
children: _buildPopupMenuActions(context, item),
),
),
),
],
),
],
),
),
],
),
);
}
Widget _buildDataCard(Map<String, dynamic> item, int sno) {
return Container(
margin: const EdgeInsets.symmetric(vertical: 6, horizontal: 8),
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: const Color(0xFFFFFFFF),
borderRadius: BorderRadius.circular(8.0),
border: Border.all(color: const Color(0xffD9EBE8)),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Title + menu
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: Text(
item['endorsement_type_value'] ?? '-',
style: _headerStyle,
),
),
PopupMenuButton<int>(
color: Colors.white,
padding: EdgeInsets.zero,
offset: const Offset(0, 30),
icon: const Icon(
Icons.more_vert,
color: Color(0xFF475569),
size: 16,
),
itemBuilder: (context) => [
PopupMenuItem<int>(
value: 1,
child: Column(
mainAxisSize: MainAxisSize.min,
children: _buildPopupMenuActions(context, item),
),
),
],
),
],
),
// Description
Text(
item['endorsement_description'] ?? '-',
style: _cardBodyStyle,
maxLines: 3,
overflow: TextOverflow.ellipsis,
),
// Status
Row(
children: [
const Text(
"Status: ",
style: TextStyle(
color: Colors.green,
fontWeight: FontWeight.w600,
fontSize: 12,
),
),
Text(
item['status'] ?? '-',
style: TextStyle(
// color: (item['status'] == "Completed")
// ? Colors.green
// : Colors.orange,
fontWeight: FontWeight.w600,
),
),
],
),
],
),
);
}
static final _dataBold = TextStyle(
fontSize: 14,
fontWeight: FontWeight.w400,
color: Color(0xFF000000),
);
static final _dataSub = TextStyle(
fontSize: 10,
fontWeight: FontWeight.w300,
color: Color(0xFF585757),
);
static const _headerStyle = TextStyle(
color: Colors.black,
fontWeight: FontWeight.bold,
);
static const _cardheaderStyle = TextStyle(
color: Colors.black,
fontWeight: FontWeight.w600,
fontSize: 12,
);
static const _cardBodyStyle = TextStyle(
color: Color(0xFF545454),
fontWeight: FontWeight.w400,
fontSize: 12,
);
}

View File

@ -547,25 +547,25 @@ class AgentState extends ConsumerState<Agent> {
// color: Colors.white,
child: Row(
children: [
IconButton(
onPressed: () {
setState(() {
docUploadedFile = null;
passportFileUrlFromApi = null;
selectedFileNames = null;
});
},
icon: Icon(
Icons.remove_circle_outline_rounded,
color: Colors.redAccent,
),
tooltip: 'To Remove Upload',
),
// IconButton(
// onPressed: () {
// setState(() {
// docUploadedFile = null;
// passportFileUrlFromApi = null;
// selectedFileNames = null;
// });
// },
// icon: Icon(
// Icons.remove_circle_outline_rounded,
// color: Colors.redAccent,
// ),
// tooltip: 'To Remove Upload',
// ),
GestureDetector(
onTap: () => apiService.downloadFile(
apiUrl:
'api/agent/downloadAgentCertificateFile?agent_id=$selectedId',
apiId: null,
apiId: selectedId,
localFile: docUploadedFile,
fileName: selectedFileNames,
),

View File

@ -13,6 +13,7 @@ class AppInputDecorations {
borderRadius: BorderRadius.circular(10),
borderSide: const BorderSide(color: Color(0xFFFFFFFF)),
),
errorStyle: const TextStyle(color: Colors.red, fontSize: 12),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
borderSide: const BorderSide(color: Color(0xFFFFFFFF)),
@ -21,6 +22,15 @@ class AppInputDecorations {
borderRadius: BorderRadius.circular(10),
borderSide: const BorderSide(color: Color(0xFF50A398), width: 2),
),
errorBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
borderSide: const BorderSide(color: Color(0xFFD83731), width: 1),
),
focusedErrorBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
borderSide: const BorderSide(color: Color(0xFFD83731), width: 2),
),
floatingLabelBehavior: FloatingLabelBehavior.never,
);
}

View File

@ -136,6 +136,9 @@ class ThemedFormField extends HookWidget {
keyboardType: keyboardType, // e.g. TextInputType.number
maxLength: maxLength, // max length
inputFormatters: inputFormatters,
// allow multiline if user sets maxLines / minLines
minLines: (keyboardType == TextInputType.multiline) ? 3 : 1,
maxLines: (keyboardType == TextInputType.multiline) ? null : 1,
),
),
);

View File

@ -83,19 +83,19 @@ class DrawerMenuState extends ConsumerState<DrawerMenu> {
),
),
),
const SizedBox(height: 5),
GestureDetector(
onTap: () {
context.go(AppRoutes.enquiryLst);
},
child: const Text(
"Enquiries",
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w400,
),
),
),
// const SizedBox(height: 5),
// GestureDetector(
// onTap: () {
// context.go(AppRoutes.enquiryLst);
// },
// child: const Text(
// "Enquiries",
// style: TextStyle(
// fontSize: 16,
// fontWeight: FontWeight.w400,
// ),
// ),
// ),
const SizedBox(height: 5),
GestureDetector(
onTap: () {
@ -230,8 +230,12 @@ class DrawerMenuState extends ConsumerState<DrawerMenu> {
builder: (context, ref, _) {
return IconButton(
onPressed: () {
ref.read(enquiryIdProvider.notifier).state = null;
context.go(AppRoutes.tabEnquiry);
if (roleId == 'manager') {
} else if (roleId == 'agent') {
context.go(AppRoutes.enquiryLst);
}
// ref.read(enquiryIdProvider.notifier).state = null;
// context.go(AppRoutes.tabEnquiry);
},
icon: const Icon(
Icons.list_alt_rounded,

View File

@ -39,15 +39,15 @@ class TopBar extends StatelessWidget implements PreferredSizeWidget {
width: 120,
),
if (isMobile) ...[
IconButton(
icon: const Icon(
Icons.arrow_back_ios,
color: Colors.black87,
size: 18,
),
onPressed: onBack ?? () => Navigator.pop(context),
),
// IconButton(
// icon: const Icon(
// Icons.arrow_back_ios,
// color: Colors.black87,
// size: 18,
// ),
// onPressed: onBack ?? () => Navigator.pop(context),
// ),
SizedBox(width: 5),
Expanded(
child: Text(
title,