nhance_partner/lib/presentation/screens/Enquiry/enquiry/quotation_tab.dart
2026-01-12 10:58:37 +05:30

1196 lines
37 KiB
Dart

import 'dart:convert';
import 'dart:io' as html;
import 'package:dropdown_search/dropdown_search.dart';
import 'package:file_picker/file_picker.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:http/http.dart' as http;
import 'package:nhance_partner/presentation/providers/userRoleProvider.dart';
import 'package:nhance_partner/presentation/screens/Enquiry/enquiry/tabs.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 '../../../providers/manager_provider.dart';
import '../../../themes/indicators/customizd_file_upload.dart';
import '../../../themes/indicators/input_field_decoration.dart';
import '../../../themes/indicators/text_field_theme.dart';
import '../../../themes/indicators/upload_doc_theme.dart'
hide ThemedUploadField;
class QuotationTab extends ConsumerStatefulWidget {
// final Map<String, dynamic>? data;
final List<Map<String, dynamic>>? data;
String? id;
final Future<void> Function()? onRefresh;
QuotationTab({super.key, this.data, this.id, this.onRefresh});
@override
ConsumerState<QuotationTab> createState() => QuotationTabState();
}
class QuotationTabState extends ConsumerState<QuotationTab> {
int currentPage = 0;
int itemsPerPage = 10;
List<Map<String, dynamic>> dataVal = [];
final TextEditingController _searchController = TextEditingController();
late ApiService apiService;
final _formKey = GlobalKey<FormState>();
final GlobalKey<DropdownSearchState<Map<String, dynamic>>> dropDownKey =
GlobalKey<DropdownSearchState<Map<String, dynamic>>>();
final GlobalKey<TabEnquiryListState> tabKey =
GlobalKey<TabEnquiryListState>();
bool blockKey = false;
List<String> tabHeader = [
'regNum',
'insurer',
'idv',
'insuranceId',
'premAmount',
'planType',
'addit_doc',
'remarks',
// 'mobile',
// 'code',
// 'address',
// 'regNo',
];
late String isActive = "1";
// html.File? docUploadedFile;
PlatformFile? docUploadedFile;
String? selectedFileNames;
String? docUploadedFileFromApi;
String? selectedId;
bool isLoading = false;
bool isquotation = false;
bool showAction = false;
Map<String, TextEditingController> controllers = {};
String? _token;
dynamic userId;
dynamic managerId;
dynamic role;
List<Map<String, dynamic>> getVehicleTypeData = [];
List<Map<String, dynamic>> filteredVechicleData = [];
List<Map<String, dynamic>> getInsurersData = [];
List<Map<String, dynamic>> filteredInsurersData = [];
Map<String, dynamic> dataDetails() {
final data = {
"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;
}
List<Map<String, dynamic>> getQuotationData = [];
List<Map<String, dynamic>> originalData = [];
List<Map<String, dynamic>> filteredData = [];
// bool isLoading = false;
@override
void initState() {
super.initState();
apiService = ApiService();
for (String field in tabHeader) {
controllers[field] = TextEditingController();
}
// updateData();
_initializeToken();
Future.microtask(() {
managerId = ref.watch(managerIdProvider);
userId = ref.watch(userIdProvider);
role = ref.watch(userRoleProvider);
getQuotationList();
});
}
Future<void> _initializeToken() async {
_token = await AuthService.getToken();
print("APISERTOKEN - $_token");
}
void refresh() {
getQuotationList();
}
@override
void dispose() {
// Dispose all TextEditingControllers
for (var controller in controllers.values) {
controller.dispose();
}
super.dispose();
}
Future<void> getQuotationList() async {
print('getClaimList called MagId - ');
setState(() {
isLoading = true;
});
try {
final response;
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('quoationListData - $getQuotationData');
blockKey = getQuotationData.any(
(item) => item['status'] == 'Accepted',
);
print('blockKey- $blockKey');
// blockKey = getQuotationData.any(
// (item) => (item['status']?.toString().toLowerCase() ?? '') == 'approved',
// );
// 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 = [];
// }
}
} catch (e) {
print('Exception occurred: $e');
} finally {
setState(() {
isLoading = false;
});
}
}
void findAndUpdateById(updateId) {
try {
final match = getQuotationData.firstWhere(
(q) => q['id'] == updateId,
orElse: () => {},
);
if (match.isNotEmpty) {
print("Found quotation: $match");
updateData(match);
} else {
print("No quotation found with id $updateId");
}
} catch (e) {
print("Error finding quotation: $e");
}
}
void updateData(Map<String, dynamic> data) {
print('UPDID - $data}');
setState(() {
controllers['regNum']?.text = data['reg_no']?.toString() ?? '';
controllers['insurer']?.text = data['insurer_name']?.toString() ?? '';
controllers['idv']?.text =
data['insured_declared_value']?.toString() ?? '';
controllers['premAmount']?.text =
data['premium_amount']?.toString() ?? '';
controllers['insuranceId']?.text =
data['insurance_plan_type_id']?.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;
}
});
}
void handleActions(data) {
print('Action - $data');
createData(data);
}
Future<void> createData(val) async {
final String apiUrldata;
apiUrldata = '${Env.apiUrl}quotation/acceptOrRejectQuotation';
// final token = await getToken(); // Fetch token
if (_token == null) {
throw Exception('Token not found. Please log in.');
}
final Map<String, dynamic> data = {
"id": selectedId,
"status": val,
"action_by": userId,
// "action_user": (role == 'agent') ? 'agent' : 'handler',
"action_user": role,
};
// data['id'] = selectedId; // Add plan_id for update
// data['status'] = val;
print("data------- $data");
try {
final response = await http.post(
Uri.parse(apiUrldata),
headers: {
'Authorization': 'Bearer $_token',
'Content-Type': 'application/json',
'app-signature': Env.App_Signature,
},
body: jsonEncode(data), // Convert map to JSON
);
if (response.statusCode == 200) {
print("Response: ${response.body}");
ToastHelper.showSuccessToast(context, 'Status Updated');
print("tre1");
selectedId = null; // just update field
isquotation = false;
print("tre11");
// 🔄 Refresh parent
if (widget.onRefresh != null) {
await widget.onRefresh!();
}
// refresh();
// WidgetsBinding.instance.addPostFrameCallback((_) {
// tabKey.currentState?.loadQuotationTab(widget.id ?? "");
// });
// context.go(AppRoutes.staffLst);
} else if (response.statusCode == 403) {
await apiService.clearLocalStorageAndRedirect();
} else {
final responseBody = jsonDecode(response.body);
dynamic msg = responseBody['data'];
ToastHelper.showErrorToast(context, 'Status Updation Failed');
print("Failed to submit plan. Status: ${response.statusCode}");
print("Error: ${response.body}");
}
} catch (e) {
print(" Error submitting Staff: $e");
}
}
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'] = Env.App_Signature;
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}");
if (widget.onRefresh != null) {
await widget.onRefresh!();
}
// context.go(AppRoutes.agentLst);
} else if (response.statusCode == 403) {
await apiService.clearLocalStorageAndRedirect();
} 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 'Pending':
return Colors.yellow;
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 SelectionArea(
child: Container(
height: MediaQuery.of(context).size.height,
width: MediaQuery.of(context).size.width,
child: filteredData.isNotEmpty
? 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: isMobile
? EdgeInsets.all(6.0)
: EdgeInsets.all(16.0),
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: 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 (!blockKey)
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')),
) );
}
Widget _buildDataTable(BuildContext context) {
if (filteredData.isEmpty) {
return const SizedBox(
height: 50,
child: Center(child: Text('No available data')),
);
}
final sortedData = [...filteredData]
..sort((a, b) => int.parse(b['id']) - int.parse(a['id']));
return SingleChildScrollView(
child: Column(
children: [
ListView.builder(
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
itemCount: ResponsiveLayout.isMobile(context)
? sortedData
.length // only cards for mobile
: sortedData.length + 1, // +1 for header in desktop
itemBuilder: (context, index) {
if (!ResponsiveLayout.isMobile(context) && index == 0) {
return _buildHeader();
}
final startIndex = (currentPage - 1) * itemsPerPage;
final item =
sortedData[index -
(ResponsiveLayout.isMobile(context) ? 0 : 1)];
final sno = startIndex + index;
return !ResponsiveLayout.isMobile(context)
? _buildDataRow(item, sno)
: _buildDataCard(item, sno);
},
),
],
),
);
}
Widget buildFormFields(BuildContext context) {
return Form(
key: _formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildResponsiveRow(
context,
buildName(context),
buildInsusrer(context),
),
if (!ResponsiveLayout.isMobile(context)) const SizedBox(height: 10),
_buildResponsiveRow(
context,
buildIdv(context),
buildPremiumAmnt(context),
),
if (!ResponsiveLayout.isMobile(context)) const SizedBox(height: 10),
_buildResponsiveRow(
context,
buildPlanType(context),
SizedBox.shrink(),
// buildDocuments(context),
),
// if (!ResponsiveLayout.isMobile(context)) const SizedBox(height: 10),
// _buildResponsiveRow(
// context,
// buildAdditonalDocuments(context),
// buildRemarks(context),
// ),
],
),
);
}
Widget _buildResponsiveRow(
BuildContext context,
Widget first,
Widget second,
) {
if (ResponsiveLayout.isMobile(context)) {
// Stack vertically
return Column(children: [first, const SizedBox(height: 2), second]);
} else {
// Place side by side
return Row(
children: [
Expanded(child: first),
const SizedBox(width: 25),
Expanded(child: second),
],
);
}
}
Widget buildName(BuildContext context) {
return buildResponsiveField(
label: "Vehicle Number",
field: ThemedFormField(
controller: controllers['regNum']!,
readOnly: true,
// validator: (value) => Validators.requiredField(value, "regNum"),
txtwidth: ResponsiveLayout.isMobile(context)
? null
: MediaQuery.of(context).size.width * 0.26,
),
);
}
Widget buildInsusrer(BuildContext context) {
return buildResponsiveField(
label: "Insurer",
field: ThemedFormField(
controller: controllers['insurer']!,
readOnly: true,
txtwidth: ResponsiveLayout.isMobile(context)
? null
: MediaQuery.of(context).size.width * 0.26,
),
);
}
Widget buildIdv(BuildContext context) {
return buildResponsiveField(
label: "IDV",
field: ThemedFormField(
controller: controllers['idv']!,
readOnly: true,
txtwidth: ResponsiveLayout.isMobile(context)
? null
: MediaQuery.of(context).size.width * 0.26,
),
);
}
Widget buildPremiumAmnt(BuildContext context) {
return buildResponsiveField(
label: "Premium Amount",
field: ThemedFormField(
controller: controllers['premAmount']!,
readOnly: true,
txtwidth: ResponsiveLayout.isMobile(context)
? null
: MediaQuery.of(context).size.width * 0.26,
),
);
}
Widget buildPlanType(BuildContext context) {
return buildResponsiveField(
label: "Plan Type",
field: ThemedFormField(
controller: controllers['planType']!,
readOnly: true,
txtwidth: ResponsiveLayout.isMobile(context)
? null
: MediaQuery.of(context).size.width * 0.26,
),
);
}
Widget buildDocuments(BuildContext context) {
return buildResponsiveUploadField(
label: 'Proposal Documents',
hintText: selectedFileNames,
onFileSelected: (fileName, file) {
setState(() {
docUploadedFile = file;
selectedFileNames = fileName;
UpdataDocuments();
});
},
showDownload: docUploadedFile != null || docUploadedFileFromApi != null,
onRemove: () {
setState(() {
docUploadedFile = null;
docUploadedFileFromApi = null;
selectedFileNames = null;
});
},
onDownload: () => apiService.downloadFile(
apiUrl: 'quotation/downloadAdditionalUploadedFile?id=$selectedId',
apiId: selectedId,
localFile: docUploadedFile,
fileName: selectedFileNames,
),
);
}
// Widget buildAdditonalDocuments(BuildContext context) {
// return buildResponsiveField(
// label: "Additional Documents",
// field: ThemedFormField(
// controller: controllers['addit_doc']!,
// // validator: (value) => Validators.phone(value, "phNumber"),
// txtwidth: ResponsiveLayout.isMobile(context)
// ? null
// : MediaQuery.of(context).size.width * 0.26,
// ),
// );
// }
//
// Widget buildRemarks(BuildContext context) {
// return buildResponsiveField(
// label: "Remarks",
// field: ThemedFormField(
// controller: controllers['remarks']!,
// // validator: (value) => Validators.phone(value, "phNumber"),
// txtwidth: ResponsiveLayout.isMobile(context)
// ? null
// : MediaQuery.of(context).size.width * 0.26,
// ),
// );
// }
Widget buildResponsiveField({required String label, required Widget field}) {
final isMobile = ResponsiveLayout.isMobile(context);
if (isMobile) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(label, style: _textStyle),
const SizedBox(height: 8),
field,
const SizedBox(height: 16),
],
);
} else {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(child: Text(label, style: _textStyle)),
const SizedBox(width: 10),
field,
],
);
}
}
// Widget buildResponsiveUploadField({
// required String label,
// required String? hintText,
// required void Function(String fileName, dynamic file) onFileSelected,
// })
// {
// final isMobile = ResponsiveLayout.isMobile(context);
//
// final uploadWidget = ThemedUploadField(
// hintText: hintText ?? "Upload Document",
// txtwidth: isMobile ? null : MediaQuery.of(context).size.width * 0.26,
// onFileSelected: onFileSelected,
// );
//
// if (isMobile) {
// return Column(
// crossAxisAlignment: CrossAxisAlignment.start,
// children: [
// Text(label, style: _textStyle),
// const SizedBox(height: 8),
// uploadWidget,
// const SizedBox(height: 16),
// ],
// );
// } else {
// return Row(
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
// children: [
// Expanded(child: Text(label, style: _textStyle)),
// const SizedBox(width: 10),
// uploadWidget,
// ],
// );
// }
// }
Widget buildResponsiveUploadField({
required String label,
required String? hintText,
required void Function(String fileName, dynamic file) onFileSelected,
bool showDownload = false,
VoidCallback? onRemove,
VoidCallback? onDownload,
}) {
final isMobile = ResponsiveLayout.isMobile(context);
final uploadWidget = ThemedUploadField(
hintText: hintText ?? "Upload Document",
txtwidth: isMobile ? null : MediaQuery.of(context).size.width * 0.26,
onFileSelected: onFileSelected,
);
final downloadRow = showDownload
? Padding(
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',
// ),
GestureDetector(
onTap: onDownload,
child: Container(
padding: const EdgeInsets.all(5),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(5),
color: const Color(0xFF425B5B),
),
child: Row(
children: const [
Text(
"Download",
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w200,
color: Colors.white,
),
),
SizedBox(width: 5),
Icon(Icons.download, size: 13, color: Colors.white),
],
),
),
),
],
),
)
: const SizedBox.shrink();
if (isMobile) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(label, style: _textStyle),
const SizedBox(height: 8),
uploadWidget,
downloadRow,
const SizedBox(height: 16),
],
);
} else {
return Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(child: Text(label, style: _textStyle)),
const SizedBox(width: 10),
uploadWidget,
],
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [SizedBox.shrink(), downloadRow],
),
],
);
}
}
Widget buildId(BuildContext context) {
return buildResponsiveField(
label: "Vehicle Number *",
field: ThemedFormField(
controller: controllers['regNo']!,
validator: (value) => Validators.requiredField(value, "regNo"),
txtwidth: ResponsiveLayout.isMobile(context)
? null
: MediaQuery.of(context).size.width * 0.26,
),
);
}
Widget _buildHeader() {
return 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(' ', style: _headerStyle)),
Expanded(flex: 1, child: Text('Vehicle Number', style: _headerStyle)),
Expanded(flex: 1, child: Text('Insurer', style: _headerStyle)),
Expanded(flex: 1, child: Text('IDV', style: _headerStyle)),
Expanded(flex: 1, child: Text('Plan Type', style: _headerStyle)),
Expanded(flex: 1, child: Text('Premium Amount', style: _headerStyle)),
Expanded(flex: 1, child: Text('Status', style: _headerStyle)),
],
),
);
}
Widget _buildDataRow(Map<String, dynamic> item, sno) {
return Container(
padding: const EdgeInsets.symmetric(vertical: 6, horizontal: 16),
// margin: const EdgeInsets.only(top: 10),
decoration: BoxDecoration(
color: Colors.white,
// color: Color(0xFFE0F7F9),
border: const Border(
bottom: BorderSide(color: Color(0xFFEAEAEA), width: 1),
),
borderRadius: BorderRadius.circular(8),
),
child: Row(
children: [
// ✅ Single-select checkbox
Expanded(
flex: 1,
child: Checkbox(
activeColor: Colors.black,
value: selectedId == item['id'], // only selected row is checked
onChanged: (val) {
setState(() {
if (val == true) {
selectedId = item['id']; // select this row
isquotation = true;
} else {
selectedId = null;
isquotation = false; // unselect
}
});
findAndUpdateById(selectedId);
print("Selected row id: $selectedId");
},
),
),
Expanded(
flex: 1,
child: Text(item['reg_no'] ?? '-', style: _dataBold),
),
Expanded(
flex: 1,
child: Text(item['insurer_name'] ?? '-', style: _dataBold),
),
Expanded(
flex: 1,
child: Text(
item['insured_declared_value'] ?? '-',
style: _dataBold,
),
),
Expanded(
flex: 1,
child: Text(item['insurance_plan_type'] ?? '-', style: _dataBold),
),
Expanded(
flex: 1,
child: Text(item['premium_amount'] ?? '-', style: _dataBold),
),
Expanded(
flex: 1,
child: Text(
item['status'] ?? '-',
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w400,
color: _getStatusColor(item['status']),
// color: item['status'] == 'Accepted' ? Colors.green : Colors.red,
),
),
),
],
),
);
}
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),
// color: const Color(0xFFF6FEFD),
borderRadius: BorderRadius.circular(8.0),
border: Border.all(color: const Color(0xffD9EBE8)),
),
child: Row(
children: [
Column(
children: [
// ✅ Single-select checkbox
Checkbox(
activeColor: Colors.black,
value: selectedId == item['id'], // only selected row is checked
onChanged: (val) {
setState(() {
if (val == true) {
selectedId = item['id']; // select this row
isquotation = true;
} else {
selectedId = null;
isquotation = false; // unselect
}
});
findAndUpdateById(selectedId);
print("Selected row id: $selectedId");
},
),
],
),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
// flex: 2,
child: Text(
item['insurer_name'] ?? '-',
style: _cardRow1BodyStyle,
),
),
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: [
TextSpan(
text: 'Status : ', // key
style: TextStyle(
color: Colors.black,
fontWeight: FontWeight.w600,
fontSize: 11,
), // key color
),
TextSpan(
text: '${item['status'] ?? '-'}', // value
style: TextStyle(
color: _getStatusColor(item['status']),
fontWeight: FontWeight.w600,
fontSize: 11,
), // value color
),
],
),
),
Text(
'${item['premium_amount']}' ?? '-',
style: _cardheaderStyle,
),
],
),
],
),
),
],
),
);
}
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 _textStyle = TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
);
static const _cardheaderStyle = TextStyle(
color: Colors.black,
fontWeight: FontWeight.w600,
fontSize: 12,
);
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),
// color: Colors.black,
fontWeight: FontWeight.w500,
fontSize: 12,
);
}