enrollment-app/lib/postFileUpload.dart
2026-01-09 10:02:34 +05:30

1263 lines
48 KiB
Dart
Executable File

import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:jwt_decode/jwt_decode.dart';
import 'package:nhancepolicy/customAppBar/customAppBar.dart';
import 'dart:convert';
import 'dart:async';
import 'package:http/http.dart' as http;
import 'package:nhancepolicy/customAppBar/toastHelper.dart';
import 'package:nhancepolicy/hrPolicyDetails.dart';
import 'package:nhancepolicy/service/api_service.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:universal_html/html.dart' as html;
import 'package:flutter/foundation.dart' show kIsWeb;
import 'dart:io';
import 'package:intl/intl.dart';
import 'package:csv/csv.dart';
import 'package:spreadsheet_decoder/spreadsheet_decoder.dart';
import 'package:url_launcher/url_launcher.dart';
import 'config/environment.dart';
import 'customAppBar/customFooter.dart';
class postFileUpload extends StatefulWidget {
final String ClientId;
final String policyTypeId;
final String ClientPoliyId;
final String clientBranchId;
final String Token;
final String TokenType;
final String cardType;
final String cardPolicyNo;
final String cardInsurer_name;
final String cardPolicy_name;
final String cardPolicy_ExpDate;
const postFileUpload(
{Key? key,
required this.ClientId,
required this.policyTypeId,
required this.ClientPoliyId,
required this.clientBranchId,
required this.Token,
required this.TokenType,
required this.cardType,
required this.cardPolicyNo,
required this.cardInsurer_name,
required this.cardPolicy_name,
required this.cardPolicy_ExpDate})
: super(key: key);
@override
State<postFileUpload> createState() => _postFileUploadState();
}
class _postFileUploadState extends State<postFileUpload> {
Uint8List? fileBytes;
Uint8List? fileBytes2;
late String _token;
dynamic getPolicyNo;
bool _isLoading = false;
dynamic getPolicyNameDetails;
dynamic clintID;
String? fileName;
int _currentStep = 0; // Step index tracker
List<dynamic> dataPolicy = [];
dynamic validationArray = [];
dynamic missingColumnErrorMsg = 0;
dynamic columnIndexMismatchCount = 0;
dynamic columnMissingCount = 0;
List<Map<String, dynamic>> extractedData = [];
dynamic argumentsData;
List<Map<String, dynamic>> originalData = []; // Original data source
List<Map<String, dynamic>> filteredData = []; // Filtered data source
List<Map<String, dynamic>> tableData = []; // Filtered data source
List<Map<String, dynamic>> nonExcelFilteredData = [];
dynamic invalidRelationships = 0;
dynamic dobAgeCheckCount = 0;
dynamic empRefId;
dynamic empPrimaryId;
List<Map<String, dynamic>> getFileUploadMasterList = [];
dynamic getThrFileList = [];
late int excelValidationStaus = 1;
bool isSuccess = false;
String successContent = '';
bool isLoading = false;
late ApiService apiService;
TextEditingController searchController = TextEditingController();
String? _selectedOption;
int _currentPage = 1;
int _rowsPerPage = 5;
List<dynamic> get _paginatedData {
final startIndex = (_currentPage - 1) * _rowsPerPage;
final endIndex =
(_currentPage * _rowsPerPage).clamp(0, filteredData.length);
return filteredData.sublist(startIndex, endIndex);
}
final List<Map<String, dynamic>> serviceList = [
{"id": 1, "name": "Sales"},
{"id": 2, "name": "Service"},
];
String? selectedKey;
String? selectedValue;
String? _selectedAction;
@override
void initState() {
super.initState();
apiService = ApiService(context);
_loadToken();
getFileUploadMasterDetails();
getFileListDetails();
}
@override
void dispose() {
super.dispose();
html.window.localStorage.remove('fileBytes');
}
Future<void> _loadToken() async {
final SharedPreferences prefs = await SharedPreferences.getInstance();
// final token = prefs.getString('hrtoken');
final token = widget.Token;
if (token != null && token.isNotEmpty) {
setState(() {
_token = token;
});
Map<String, dynamic>? decodedToken = Jwt.parseJwt(token);
print('decodedToken $decodedToken');
} else {
// Token is empty or null, handle accordingly (e.g., navigate to login screen)
// For now, let's navigate to the login screen
ToastHelper.showErrorToast(context, 'Session Out');
Navigator.pushReplacementNamed(context, 'hrLogin');
}
}
// Future<void> getPolicyDetails() async {
// setState(() {
// clientPolicyId = argumentsData['client_policy_id'];
// clientId = argumentsData['client_id'];
// policyType = argumentsData['type'];
// policy_name = argumentsData['policy_name'];
// });
// }
// Future<void> _uploadFile1(importPolicyName) async {
// FilePickerResult? result = await FilePicker.platform.pickFiles(
// type: FileType.custom,
// allowedExtensions: ['xlsx', 'xls', 'csv'],
// );
//
// if (result != null) {
// PlatformFile file = result.files.first;
// Uint8List fileBytes = file.bytes!;
// // Use the fileBytes as needed
// print('File name: ${file.name}');
// print('File size: ${file.size}');
// print('File bytes: $fileBytes');
// _processExcelData(fileBytes);
// } else {
// // User canceled the picker
// }
// }
Future<void> getFileUploadMasterDetails() async {
print('9');
try {
final response = await apiService.getFileUploadMastersToApi(widget.Token);
if (response['status'] == true) {
print('getFileUploadMasterList1');
setState(() {
final actions = Map<String, String>.from(response['data']['actions']);
setState(() {
getFileUploadMasterList = actions.entries
.map((e) => {"key": e.key, "value": e.value})
.toList();
print('getFileUploadMasterList: $getFileUploadMasterList');
});
print('getFileUploadMasterList');
print(getFileUploadMasterList);
});
} else {
print('Request failed with status: ${response['code']}');
}
} catch (e) {
setState(() {
isLoading = false;
});
print('Exception occurred: $e');
} finally {
setState(() {
// _isLoading = false;
});
}
}
Future<void> getFileListDetails() async {
final SharedPreferences prefs = await SharedPreferences.getInstance();
empPrimaryId = prefs.getString('empPrimaryId');
print('9');
try {
final response = await apiService.getFileListToApi(
empPrimaryId, widget.cardPolicyNo, widget.Token);
if (response['status'] == 'success') {
print('getThrFileList');
setState(() {
getThrFileList = List<Map<String, dynamic>>.from(response['data']);
originalData = getThrFileList;
filteredData = List.from(originalData);
print('filteredData');
print(filteredData);
});
} else {
print('Request failed with status: ${response['code']}');
}
} catch (e) {
setState(() {
isLoading = false;
});
print('Exception occurred: $e');
} finally {
setState(() {
// _isLoading = false;
});
}
}
Future<void> getHrFileDownload(id, file_name) async {
// final http.Response response = await apiService.getHrFileDownloadToApi(id, widget.Token);
final apiurl = Environment.apiUrlPost;
final String url = '$apiurl/hrFileDownload?id=$id';
final token = widget.Token;
final response = await http.get(
Uri.parse(url),
headers: {
'APP-SIGNATURE': 'nhance-2025-signature-T8f6gV9k5fK2wf5V5c4vId3b0J8z6cAm2x8Y',
'Authorization': 'Bearer $token',
'Content-Type': 'application/json',
// 'app-signature': 'ts-traveltool-2025-signature-123456',
},
);
if (response.statusCode == 200) {
try {
print("PDF Downloaded");
// ✅ Create a blob from the response body bytes
final blob = html.Blob([response.bodyBytes]);
// ✅ Generate a download URL
final url = html.Url.createObjectUrlFromBlob(blob);
// ✅ Trigger file download automatically
final anchor = html.AnchorElement(href: url)
..setAttribute('download', '$file_name')
..click();
// ✅ Revoke the URL to free memory
html.Url.revokeObjectUrl(url);
} catch (e) {
throw Exception('Error parsing response: $e');
}
} else {
print("Download failed with status: ${response.statusCode}");
}
}
void _uploadFile() async {
print('Test');
if (kIsWeb) {
print('kIsWeb');
final input = html.FileUploadInputElement();
input.accept = '.xlsx,.xls,.csv';
input.click();
input.onChange.listen((event) async {
final file = input.files!.first;
final reader = html.FileReader();
reader.readAsArrayBuffer(file);
await reader.onLoadEnd.first; // Wait for the file to be loaded
if (reader.readyState == html.FileReader.DONE) {
Uint8List? fileBytes = reader.result as Uint8List?;
if (fileBytes != null) {
setState(() {
fileName = file.name;
});
// Save fileBytes to local storage
final jsonString = json.encode(fileBytes);
html.window.localStorage['fileBytes'] = jsonString;
print('File Name: $fileName');
print('File Bytes: $fileBytes');
sendExcelFIleTOAPI(fileBytes, fileName);
// Call the function to process Excel data here
// _processExcelData(fileBytes, fileName);
}
}
});
} else {
// Handle non-web platforms here (e.g., show an error message)
print('File upload is only supported on web platforms.');
}
}
int countInvalidDobs(List<Map<String, dynamic>> data) {
int invalidCount = 0;
for (var entry in data) {
DateTime dob;
// if (entry['DOB'] is String) {
dob = DateTime.parse(entry['DOB'].toString());
// } else if (entry['DOB'] is DateTime) {
// dob = entry['DOB'];
// } else {
// // Invalid DOB format, skip this entry
// continue;
// }
print(entry['Relation']);
if ((entry['Relation'].toString() == 'Son' ||
entry['Relation'].toString() == 'Daughter')) {
if (DateTime.now().difference(dob).inDays > 25 * 365) {
print('child $dob');
invalidCount++;
}
} else if ((entry['Relation'] != 'Son' &&
entry['Relation'] != 'Daughter')) {
if (DateTime.now().difference(dob).inDays < 18 * 365) {
print('others $dob');
invalidCount++;
}
}
}
return invalidCount;
}
void _dragAndDropFile(html.File file) async {
print('file');
print(file);
// Prepare form data
final formData = html.FormData();
formData.appendBlob('file', file);
// Send formData to API endpoint
final response = await html.HttpRequest.request(
'your_api_endpoint_here',
method: 'POST',
sendData: formData,
);
// Handle response as needed
print(response.responseText);
}
Future<void> sendExcelFIleTOAPI(Uint8List fileBytes, fileName) async {
final SharedPreferences prefs = await SharedPreferences.getInstance();
empPrimaryId = prefs.getString('empPrimaryId');
// Future.delayed(Duration(seconds: 3), () {
setState(() {
isLoading = true;
});
// });
print('submit');
print(fileBytes);
if (fileBytes == null) {
ToastHelper.showErrorToast(context, 'Please upload file');
print('return');
return; // No file selected
} else {
print('else');
// // Prepare form data
// final formData = html.FormData();
// formData.appendBlob('file', html.Blob([fileBytes]), fileName);
// URL of the API where you want to send the file
final apiUrl = Environment.apiUrlPost + 'hrFileUpload';
print('else');
// Create a multipart request
final request = http.MultipartRequest('POST', Uri.parse(apiUrl));
print('else');
// Attach the file to the request
// Set authorization token in headers
request.headers['APP-SIGNATURE'] = 'nhance-2025-signature-T8f6gV9k5fK2wf5V5c4vId3b0J8z6cAm2x8Y';
request.headers['Authorization'] = 'Bearer $_token';
// request.files.add(http.MultipartFile.fromBytes('file', fileBytes,
// filename: fileName));
print('Filename: $fileName');
// request.files.add(http.MultipartFile.fromBytes(
// 'file',
// fileBytes,
// filename: fileName ?? 'default_filename.xlsx',
// ));
request.files.add(http.MultipartFile.fromBytes(
'file_name',
fileBytes,
filename: fileName ?? 'default_filename.xlsx',
));
print('clintID: $clintID');
request.fields['client_id'] = widget.ClientId;
request.fields['policy_no'] = widget.cardPolicyNo;
request.fields['client_branch_id'] = widget.clientBranchId;
request.fields['file_action'] = selectedKey!;
// request.fields['status'] = selectedKey!;
request.fields['created_by'] = empPrimaryId;
request.fields['policy_id'] = widget.ClientPoliyId;
// "client_id": 1,
// "client_branch_id": 2,
// "policy_no": "POL123456",
// "file_action": ,
// "status": "inception",
// "created_by": 10
print('request : $request');
// Send the request
final response = await request.send();
print('else');
// Read response stream as a string
final responseString = await response.stream.bytesToString();
print(responseString);
Map<String, dynamic> data = json.decode(responseString);
if (data['status'] == true) {
print('upload success');
setState(() {
isLoading = false;
});
ToastHelper.showSuccessToast(context, data['message']);
getFileListDetails();
setState(() {
selectedValue = null;
selectedKey = null;
resetErrorCount();
});
} else {
setState(() {
isLoading = false;
});
ToastHelper.showErrorToast(context, data['message']);
}
}
}
resetErrorCount() {
setState(() {
fileBytes = null;
fileName = null;
});
}
void search(String query) {
print(query);
// Check if the query is empty
if (query.isEmpty) {
// If search query is empty, show all data
setState(() {
filteredData = List.from(originalData);
});
} else {
// Filter the original data based on the search query
setState(() {
filteredData = originalData.where((row) {
// Implement your filter logic here
// For example, check if any field in the row contains the query
// Adjust this logic based on your data structure
return row['file_name']
.toString()
.toLowerCase()
.contains(query.toLowerCase()) ||
row['file_action']
.toString()
.toLowerCase()
.contains(query.toLowerCase()) ||
row['created_at']
.toString()
.toLowerCase()
.contains(query.toLowerCase());
}).toList();
});
}
print(filteredData.length);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: CustomAppBar(),
backgroundColor: Color(0xFFEFF3F6),
body: Stack(children: [
// color: Color(0xFFEFF3F6),
// padding: const EdgeInsets.symmetric(vertical: 20, horizontal: 40),
SingleChildScrollView(
scrollDirection: Axis.vertical,
child: Container(
padding: EdgeInsets.only(top: 30, bottom: 200, left: 50, right: 50),
child: Column(
children: [
Container(
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment:
CrossAxisAlignment.center, // 👈 fix alignment
children: [
Expanded(
flex: 8,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// back button + texts
Row(
children: [
IconButton(
padding: EdgeInsets.zero,
constraints: BoxConstraints(),
onPressed: () {
Navigator.pop(context);
},
icon: Icon(
Icons.arrow_back_ios,
color: Colors.grey,
size: 17,
),
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"${widget.cardType} - ${widget.cardPolicyNo}",
style: GoogleFonts.poppins(
color: Colors.black,
fontSize: 14,
fontWeight: FontWeight.w500,
),
),
Text(
widget.TokenType == 'pre'
? "${widget.cardPolicy_name} (${widget.cardPolicy_ExpDate})"
: "${widget.cardInsurer_name} - ${widget.cardPolicy_name} (${widget.cardPolicy_ExpDate})",
style: GoogleFonts.poppins(
color: Colors.grey,
fontSize: 12,
fontWeight: FontWeight.w400,
),
),
],
)
],
),
],
),
),
Expanded(
flex: 4,
child: Container(
padding: const EdgeInsets.only(top: 4),
child: getFileUploadMasterList.isEmpty
? const SizedBox() // 👈 avoid crash on first build
: buildDropdownField<String>(
'File Action',
(value) {
setState(() {
selectedKey = value;
selectedValue = getFileUploadMasterList
.firstWhere((item) =>
item['key'] == value)['value'];
});
debugPrint("Selected Key: $selectedKey");
debugPrint(
"Selected Value: $selectedValue");
},
false,
getFileUploadMasterList,
'value',
selectedKey,
valueKey: 'key',
),
),
),
],
),
),
SizedBox(
height: 20,
),
Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius:
BorderRadius.circular(10), // 👈 set your desired radius
),
// height: 400,
// margin: const EdgeInsets.only(left: 40.0, right: 40.0),
padding: const EdgeInsets.all(16.0),
child: Column(
children: [
Container(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Expanded(
flex: 12,
child: Column(
children: [
// Row(
// mainAxisAlignment:
// MainAxisAlignment.center,
// children: [
// Expanded(
// child: Text(
// '${widget.cardPolicy_name} - (${widget.cardType})',
// textAlign: TextAlign.center,
// style: const TextStyle(
// fontSize: 20,
// fontWeight: FontWeight
// .w600, // Adjust the font size as needed
// color: Color(
// 0xFF181818), // Adjust the text color as needed
// ),
// ),
// )
// ],
// ),
SizedBox(height: 20),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Expanded(
child: Container(
margin: EdgeInsets.symmetric(
horizontal: 150),
alignment: Alignment.center,
color: Colors.grey[200],
height: 180,
child: DragTarget(
onAccept: (html.File droppedFile) {
setState(() {
fileName = droppedFile.name;
});
_dragAndDropFile(droppedFile);
},
builder: (BuildContext context,
List<String?> candidateData,
List<dynamic> rejectedData) {
return Container(
alignment: Alignment.center,
child: Column(
mainAxisAlignment:
MainAxisAlignment.center,
children: [
fileName != null
? Column(
mainAxisAlignment:
MainAxisAlignment
.center,
children: [
Icon(
Icons
.upload_file, // Choose the appropriate icon
size: 35,
color: Color(
0xFFE26728), // Adjust the size as needed
),
SizedBox(
height:
15), // Add some space between the icon and text
Text(
'$fileName',
style: TextStyle(
fontSize:
16),
),
SizedBox(
height:
25), // Add some space between the icon and text
MouseRegion(
cursor: SystemMouseCursors
.click, // Set cursor to pointer on hover
child:
GestureDetector(
onTap: () {
resetErrorCount();
},
child:
const Row(
mainAxisAlignment:
MainAxisAlignment
.center,
children: [
Icon(
Icons
.delete_forever, // Choose the remove icon
size:
20, // Adjust the size as needed
color: Colors
.red, // Set the color of the remove icon
),
SizedBox(
width:
1), // Add some space between the icon and text
Text(
'Remove',
style: TextStyle(
fontSize:
13,
color:
Color(0xFF727272)),
),
],
),
),
)
],
)
: ElevatedButton(
onPressed: () => {
print(
'selectedValue $selectedValue'),
if (selectedValue !=
null)
{
_uploadFile(),
}
else
{
ToastHelper
.showErrorToast(
context,
'Please select file action')
}
},
child: Text(
'Select File',
style: TextStyle(
color: Colors
.white),
),
style:
ElevatedButton
.styleFrom(
backgroundColor:
Color(
0xFFE26728),
),
),
SizedBox(height: 20),
if (fileName == null)
Text(
'Supported Files : XLSX')
// Add more Text widgets for additional lines of text
],
),
);
},
),
),
),
],
),
],
),
),
],
),
),
],
),
),
SizedBox(height: 10),
Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius:
BorderRadius.circular(10), // 👈 set your desired radius
),
// width: double.infinity, // take full width
// height: MediaQuery.of(context).size.height * 0.8, // 80% of screen height
padding: const EdgeInsets.all(16.0),
child: Column(
children: [
Row(
children: [
Expanded(
flex: 4,
child: Align(
alignment: Alignment.centerLeft,
child: Container(
width: 400,
height: 37,
decoration: BoxDecoration(
color: Color(0xFFF0F0F0),
borderRadius: BorderRadius.circular(8),
),
child: TextField(
decoration: InputDecoration(
hintText: 'Search',
prefixIcon: Icon(Icons.search, size: 18),
contentPadding: EdgeInsets.symmetric(
horizontal: 12, vertical: 8),
border: InputBorder
.none, // No border since Container handles it
),
controller: searchController,
onChanged: search,
style: TextStyle(fontSize: 14),
),
),
),
),
],
),
SizedBox(height: 20),
Row(
children: [
Expanded(
child: _buildFileUploadedListTable(context),
)
],
),
],
),
),
],
),
),
),
if (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
),
),
Align(
alignment: Alignment.bottomCenter,
child: Container(
width: double.infinity, // Make the footer full width
child: CustomFooter(),
),
),
]),
);
}
Widget buildDropdownField<T>(
String label,
void Function(T?) onChanged,
bool readOnly,
List<Map<String, dynamic>> itemsList,
String displayField,
T? selectedValue, {
String valueKey = 'id',
}) {
return DropdownButtonFormField<T>(
value: selectedValue,
decoration: InputDecoration(labelText: label),
items: itemsList.map<DropdownMenuItem<T>>((item) {
return DropdownMenuItem<T>(
value: item[valueKey] as T, // ✅ cast to generic type
child:
Text(item[displayField].toString()), // always display as String
);
}).toList(),
onChanged: readOnly ? null : onChanged,
validator: (value) {
if (value == null) {
return 'Please select a $label';
}
return null;
},
);
}
Widget _buildFileUploadedListTable(BuildContext context) {
// Loader overlay
if (filteredData.isEmpty) {
return const SizedBox(
height: 50,
child: Center(child: Text('No available data')),
);
}
print('filteredData12345 $filteredData');
return ListView.builder(
shrinkWrap: true, // important for nested lists
physics: const AlwaysScrollableScrollPhysics(),
itemCount: _paginatedData.length + 2,
itemBuilder: (context, index) {
if (index == 0) return _buildHeader();
if (index == _paginatedData.length + 1)
return _buildPagination(context);
final item = _paginatedData[index - 1];
return _buildDataRow(item);
},
);
// return Column(
// crossAxisAlignment: CrossAxisAlignment.start,
// children: [
// // Header row
// Container(
// decoration: BoxDecoration(
// color: Color(0xFF00A6A6),
// borderRadius: BorderRadius.circular(6),
// ),
// padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 16),
// child: Row(
// children: [
// Expanded(
// flex: 4,
// child: Text(
// 'Insurer Name',
// style: GoogleFonts.poppins(
// color: Colors.white, fontWeight: FontWeight.bold),
// ),
// ),
// Expanded(
// flex: 3,
// child: Text(
// 'CD Account number',
// style: GoogleFonts.poppins(
// color: Colors.white, fontWeight: FontWeight.bold),
// ),
// ),
// Expanded(
// flex: 2,
// child: Text(
// 'Current Balance',
// style: GoogleFonts.poppins(
// color: Colors.white, fontWeight: FontWeight.bold),
// ),
// ),
// Expanded(
// flex: 1,
// child: Text(
// 'Action',
// textAlign: TextAlign.center,
// style: GoogleFonts.poppins(
// color: Colors.white, fontWeight: FontWeight.bold),
// ),
// ),
// ],
// ),
// ),
//
// const SizedBox(height: 6),
//
// // Table body rows
// ..._paginatedData.mapIndexed((index, item) {
// return Container(
// margin: const EdgeInsets.only(bottom: 8),
// padding: const EdgeInsets.symmetric(vertical: 5, horizontal: 16),
// decoration: BoxDecoration(
// color: index % 2 == 0 ? Color(0xFFE6FAFB) : Colors.white,
// borderRadius: BorderRadius.circular(6),
// ),
// child: Row(
// children: [
// Expanded(
// flex: 4,
// child: Text(
// item['insurer_name'] ?? '-',
// style: GoogleFonts.poppins(color: Color(0xFF000000)),
// ),
// ),
// Expanded(
// flex: 3,
// child: Text(
// item['cd_master_account_no'] ?? '-',
// style: GoogleFonts.poppins(color: Color(0xFF000000)),
// ),
// ),
// Expanded(
// flex: 2,
// child: Text(
// "₹${item['balance'] ?? '0'}",
// style: GoogleFonts.poppins(color: Color(0xFF000000)),
// ),
// ),
// Expanded(
// flex: 1,
// child: Center(
// child: IconButton(
// icon: const Icon(Icons.remove_red_eye_outlined),
// onPressed: () {
// Navigator.push(
// context,
// MaterialPageRoute(
// builder: (context) => cdTransactionDetails(
// insurerName: item['insurer_name'],
// cdMasterAccountNo: item['cd_master_account_no'],
// insurerId: item['insurer_id'],
// cd_ac_pk: item['cd_ac_pk'],
// empClientId: widget.empClientId,
// postToken: widget.postToken),
// ),
// );
// },
// ),
// ),
// ),
// ],
// ),
// );
// }).toList(),
//
//
// ],
// );
}
Widget _buildDataRow(Map<String, dynamic> item) {
print('item $item');
return Container(
padding: const EdgeInsets.symmetric(vertical: 6, horizontal: 16),
margin: const EdgeInsets.only(top: 10),
decoration: BoxDecoration(
color: Color(0xFFE0F7F9),
border: const Border(
bottom: BorderSide(color: Color(0xFFD7E9EB), width: 1),
),
borderRadius: BorderRadius.circular(8),
),
child: Row(
children: [
Expanded(
flex: 4,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
MouseRegion(
cursor: SystemMouseCursors.click, // 👆 Pointer cursor
child: Tooltip(
message: "Click to Download", // 📝 Tooltip text
child: GestureDetector(
onTap: () {
getHrFileDownload(
item['id'],
item[
'file_name']); // ✅ Call your API/download logic
},
child: Text(
item['file_name'] ?? '-',
style: _dataBold.copyWith(
color: Colors.blue, // 🔗 Make it look like a link
decoration: TextDecoration.underline,
),
),
),
),
),
],
),
),
Expanded(
flex: 3,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(item['file_action'] ?? '-', style: _dataBold),
],
),
),
Expanded(
flex: 2,
child: Text(formatDate(item['created_at']), style: _dataBold),
),
Expanded(
flex: 2,
child: Text(item['status'], style: _dataBold),
),
],
),
);
}
Widget _buildHeader() {
return Container(
decoration: BoxDecoration(
color: Color(0xFF00A6A6),
borderRadius: BorderRadius.circular(6),
),
padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 16),
child: const Row(
children: [
Expanded(flex: 4, child: Text('File Name', style: _headerStyle)),
Expanded(flex: 3, child: Text('File Action', style: _headerStyle)),
Expanded(flex: 2, child: Text('Created At', style: _headerStyle)),
Expanded(flex: 2, child: Text('Status', style: _headerStyle))
],
),
);
}
static final _dataBold = GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w400,
color: Color(0xFF000000),
);
static final _dataSub = GoogleFonts.poppins(
fontSize: 10,
fontWeight: FontWeight.w300,
color: Color(0xFF585757),
);
static const _headerStyle = TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
);
Widget _buildPagination(BuildContext context) {
final totalPages = (filteredData.length / _rowsPerPage).ceil();
const visiblePageCount = 5;
List<int> getVisiblePages() {
if (totalPages <= visiblePageCount) {
return List.generate(totalPages, (i) => i + 1);
}
if (_currentPage <= 3) {
return [1, 2, 3, 4, 5];
} else if (_currentPage >= totalPages - 2) {
return [
totalPages - 4,
totalPages - 3,
totalPages - 2,
totalPages - 1,
totalPages
];
} else {
return [
_currentPage - 2,
_currentPage - 1,
_currentPage,
_currentPage + 1,
_currentPage + 2,
];
}
}
List<int> visiblePages = getVisiblePages();
return Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Padding(
padding: const EdgeInsets.symmetric(vertical: 12),
child: Row(
children: [
// Dropdown for rows per page
DropdownButton<int>(
value: _rowsPerPage,
items: [5, 10, 15, 20, 50].map((int value) {
return DropdownMenuItem<int>(
value: value,
child: Text(' $value ',
style: GoogleFonts.poppins(fontSize: 15)),
);
}).toList(),
onChanged: (newValue) {
setState(() {
_rowsPerPage = newValue!;
_currentPage = 1;
});
},
),
// Previous button
IconButton(
onPressed: _currentPage > 1
? () => setState(() => _currentPage--)
: null,
icon: const Icon(Icons.chevron_left),
),
// First page + left ellipsis
if (!visiblePages.contains(1))
Row(children: [
_buildPageButton(1),
const Padding(
padding: EdgeInsets.symmetric(horizontal: 4),
child: Text("..."),
),
]),
// Visible page buttons
for (int page in visiblePages) _buildPageButton(page),
// Right ellipsis + last page
if (!visiblePages.contains(totalPages))
Row(children: [
const Padding(
padding: EdgeInsets.symmetric(horizontal: 4),
child: Text("..."),
),
_buildPageButton(totalPages),
]),
// Next button
IconButton(
onPressed: _currentPage < totalPages
? () => setState(() => _currentPage++)
: null,
icon: const Icon(Icons.chevron_right),
),
],
),
),
],
);
}
Widget _buildPageButton(int page) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 4),
child: ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor:
_currentPage == page ? const Color(0xFF00A6A6) : Colors.grey[300],
foregroundColor: _currentPage == page ? Colors.white : Colors.black,
minimumSize: const Size(36, 36),
padding: EdgeInsets.zero,
),
onPressed: () {
setState(() {
_currentPage = page;
});
},
child: Text(page.toString()),
),
);
}
String formatDate(String? dateString) {
if (dateString == null || dateString.isEmpty) return '-';
try {
DateTime parsedDate = DateTime.parse(dateString);
return DateFormat('dd-MM-yyyy hh:mm a').format(parsedDate);
} catch (e) {
return '-';
}
}
}