enrollment-app/lib/presentation/preFileUpload.dart

1886 lines
73 KiB
Dart
Executable File

import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:jwt_decode/jwt_decode.dart';
import 'dart:convert';
import 'dart:async';
import 'package:http/http.dart' as http;
import 'package:nhancepolicy/customAppBar/toastHelper.dart';
import 'package:nhancepolicy/service/api_service.dart';
import 'package:nhancepolicy/service/token_storage_service.dart';
import 'package:universal_html/html.dart' as html;
import 'package:flutter/foundation.dart' show kIsWeb;
// import 'package:excel/excel.dart';
import 'package:excel/excel.dart' hide Border, TextSpan;
import 'dart:io';
import 'package:intl/intl.dart';
import 'package:csv/csv.dart';
import '../config/environment.dart';
import '../customAppBar/base_layout.dart';
import 'excelVerification.dart';
import 'hrPolicyDetails.dart';
import 'package:nhancepolicy/logger.dart';
class preFileUpload 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;
final String total_premium;
const preFileUpload({
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,
required this.total_premium,
}) : super(key: key);
@override
State<preFileUpload> createState() => _excelVerifyState();
}
class _excelVerifyState extends State<preFileUpload> {
final tokenService = TokenStorageService();
String localClientId = '';
String localPolicyTypeId = '';
String localClientPolicyId = '';
String localClientBranchId = '';
String localToken = '';
String localTokenType = '';
String localCardType = '';
String localCardPolicyNo = '';
String localCardInsurerName = '';
String localCardPolicyName = '';
String localCardPolicyExpDate = '';
String localTotalPremium = '';
Uint8List? fileBytes;
Uint8List? fileBytes2;
late String _token;
dynamic getPolicyNo;
bool _isLoading = false;
dynamic getPolicyNameDetails;
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;
List<String> excelHeader = [];
List<List<Map<String, dynamic>>> excelData = [];
late int excelValidationStaus = 1;
bool isSuccess = false;
String successContent = '';
bool isLoading = false;
late ApiService apiService;
final TextEditingController openDateController = TextEditingController();
final TextEditingController closeDateController = TextEditingController();
dynamic getThrFileList = [];
final List<String> _allowedExtensions = ['xlsx', 'xls'];
int _currentPage = 1;
int _rowsPerPage = 6;
List<dynamic> get _paginatedData {
final startIndex = (_currentPage - 1) * _rowsPerPage;
final endIndex =
(_currentPage * _rowsPerPage).clamp(0, filteredData.length);
return filteredData.sublist(startIndex, endIndex);
}
@override
void initState() {
super.initState();
apiService = ApiService(context);
restoreUploadData().then((_) {
_loadToken();
getFileListDetails();
});
}
@override
void dispose() {
super.dispose();
html.window.localStorage.remove('fileBytes');
}
Future<void> _loadToken() async {
final token = localToken.isNotEmpty
? localToken
: await tokenService.readValue('upload_Token');
if (token != null && token.isNotEmpty) {
setState(() {
_token = token;
});
} else {
ToastHelper.showErrorToast(context, 'Session Out');
Navigator.pushReplacementNamed(context, 'hrLogin');
}
}
Future<void> restoreUploadData() async {
localClientId = widget.ClientId.isNotEmpty
? widget.ClientId
: await tokenService.readValue('upload_ClientId') ?? '';
localPolicyTypeId = widget.policyTypeId.isNotEmpty
? widget.policyTypeId
: await tokenService.readValue('upload_policyTypeId') ?? '';
localClientPolicyId = widget.ClientPoliyId.isNotEmpty
? widget.ClientPoliyId
: await tokenService.readValue('upload_ClientPoliyId') ?? '';
localClientBranchId = widget.clientBranchId.isNotEmpty
? widget.clientBranchId
: await tokenService.readValue('upload_clientBranchId') ?? '';
localToken = widget.Token.isNotEmpty
? widget.Token
: await tokenService.readValue('upload_Token') ?? '';
localTokenType = widget.TokenType.isNotEmpty
? widget.TokenType
: await tokenService.readValue('upload_TokenType') ?? '';
localCardType = widget.cardType.isNotEmpty
? widget.cardType
: await tokenService.readValue('upload_cardType') ?? '';
localCardPolicyNo = widget.cardPolicyNo.isNotEmpty
? widget.cardPolicyNo
: await tokenService.readValue('upload_cardPolicyNo') ?? '';
localCardInsurerName = widget.cardInsurer_name.isNotEmpty
? widget.cardInsurer_name
: await tokenService.readValue('upload_cardInsurer_name') ?? '';
localCardPolicyName = widget.cardPolicy_name.isNotEmpty
? widget.cardPolicy_name
: await tokenService.readValue('upload_cardPolicy_name') ?? '';
localCardPolicyExpDate = widget.cardPolicy_ExpDate.isNotEmpty
? widget.cardPolicy_ExpDate
: await tokenService.readValue('upload_cardPolicy_ExpDate') ?? '';
localTotalPremium = widget.total_premium.isNotEmpty
? widget.total_premium
: await tokenService.readValue('upload_total_premium') ?? '';
}
Future<void> clearUploadStorage() async {
await tokenService.removeValue('upload_ClientId');
await tokenService.removeValue('upload_policyTypeId');
await tokenService.removeValue('upload_ClientPoliyId');
await tokenService.removeValue('upload_clientBranchId');
await tokenService.removeValue('upload_Token');
await tokenService.removeValue('upload_TokenType');
await tokenService.removeValue('upload_cardType');
await tokenService.removeValue('upload_cardPolicyNo');
await tokenService.removeValue('upload_cardInsurer_name');
await tokenService.removeValue('upload_cardPolicy_name');
await tokenService.removeValue('upload_cardPolicy_ExpDate');
await tokenService.removeValue('upload_total_premium');
}
// 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
// logDebug('File name: ${file.name}');
// logDebug('File size: ${file.size}');
// logDebug('File bytes: $fileBytes');
// _processExcelData(fileBytes);
// } else {
// // User canceled the picker
// }
// }
void _uploadFile(importPolicyName) async {
logDebug('Test');
if (kIsWeb) {
logDebug('kIsWeb');
final input = html.FileUploadInputElement();
input.accept = '.xlsx,.xls';
input.click();
input.onChange.listen((event) async {
final file = input.files?.first;
if (file == null) return;
final fileExtension = file.name.split('.').last.toLowerCase();
// ❌ INVALID FORMAT
if (!_allowedExtensions.contains(fileExtension)) {
ToastHelper.showErrorToast(
context,
'File format not supported. Please upload XLSX or XLS',
);
resetErrorCount();
return; // 🚫 STOP HERE
}
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;
logDebug('File Name: $fileName');
logDebug('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)
logDebug('File upload is only supported on web platforms.');
}
}
bool _validateDatesBeforeUpload() {
if (openDateController.text.isEmpty || closeDateController.text.isEmpty) {
ToastHelper.showErrorToast2(
context, '', 'Please select both Enrolment Open Date and Close Date');
return false;
}
return true;
}
void _processExcelData(Uint8List fileBytes, fileName) {
List<List<Data>> dataArray;
if (fileName.endsWith('.xlsx')) {
dataArray = decodeExcelData(fileBytes);
} else if (fileName.endsWith('.xls')) {
dataArray = decodeXLSData(fileBytes);
} else if (fileName.endsWith('.csv')) {
logDebug('csv');
dataArray = decodeCSVData(fileBytes);
} else {
throw UnsupportedError('Unsupported file format: $fileName');
}
logDebug('_processExcelData');
// Decode the Excel file and extract relevant data
// Assuming dataArray is your array containing Excel data
// List<List<Data>> dataArray = decodeExcelData(fileBytes);
logDebug(dataArray);
// logDebug(dataArray[0].toString());
// Extract Name, Age, and City from the array
logDebug(dataArray[0].length);
if (dataArray[0].length == 11) {
for (int i = 0; i < dataArray.length; i++) {
Map<String, dynamic> dataMap = {
"Sno": dataArray[i][0].value,
"Emp_Code": dataArray[i][1].value,
"Name": dataArray[i][2].value,
"DOJ": dataArray[i][3].value,
"Gender": dataArray[i][4].value,
"Relation": dataArray[i][5].value,
"DOB": dataArray[i][6].value,
"Mail": dataArray[i][7].value,
"Mobile": dataArray[i][8].value,
"SI": dataArray[i][9].value,
"Grade": dataArray[i][10].value,
};
if (i == 0) {
// logDebug(dataMap);
validationArray.add(dataMap);
} else {
// logDebug(dataMap);
extractedData.add(dataMap);
}
}
// Do something with extracted data (e.g., display in UI)
originalData = extractedData;
filteredData = List.from(originalData);
logDebug('filteredData');
logDebug(filteredData);
logDebug(validationArray);
validationArray[0].forEach((key, value) {
logDebug(key);
logDebug(value);
if (key.toString().trim().toLowerCase() !=
value.toString().trim().toLowerCase()) {
// If key and value are not equal, increment mismatch count
columnIndexMismatchCount++;
logDebug('columnIndexMismatchCount: $value');
}
if (value.toString() == 'null') {
// If value is null, increment missing count
columnMissingCount++;
logDebug('Value: $value');
}
});
logDebug('columnIndexMismatchCount: $columnIndexMismatchCount');
logDebug('columnMissingCount: $columnMissingCount');
nonExcelFilteredData = filteredData.where((item) {
final relation = item['Relation'];
return relation != null &&
relation.toString().trim().toLowerCase() != 'self';
}).toList();
logDebug('nonExcelFilteredData');
logDebug(nonExcelFilteredData);
logDebug(nonExcelFilteredData.length);
if (argumentsData['type'] == 'GPA') {
if (nonExcelFilteredData.length > 0) {
logDebug('nonSelf');
invalidRelationships = nonExcelFilteredData.length;
} else {
logDebug('Self');
invalidRelationships = nonExcelFilteredData.length;
}
logDebug('invalidRelationships: $invalidRelationships');
} else {
invalidRelationships = 0;
}
int invalidDobCount = countInvalidDobs(filteredData);
logDebug('Number of invalid DOBs: $invalidDobCount');
dobAgeCheckCount = invalidDobCount;
} else {
logDebug('Some Column is Missing');
var columnMissingCount = 11 - dataArray[0].length;
missingColumnErrorMsg = columnMissingCount;
logDebug(missingColumnErrorMsg);
}
}
// bool checkAllSelf(List<Map<String, dynamic>> dataList) {
// // Check if any value of 'Relation' key is not 'Self'
// bool allSelf = dataList.every((data) => data['Relation'] == 'Self');
// // If all values are 'Self', return true; otherwise, return false
// return allSelf;
// }
// Placeholder function for decoding Excel data
List<List<Data>> decodeExcelData(Uint8List fileBytes) {
logDebug('decodeExcelData');
// Create an Excel instance from the fileBytes
final excel = Excel.decodeBytes(fileBytes);
logDebug('decodeExcelData');
logDebug(excel);
// Assuming there's only one sheet in the Excel file
final sheet = excel.tables.keys.first;
final table = excel.tables[sheet]!;
// Convert Excel table to a List<List<Data>>
// Convert Excel table to a List<List<Data>>
List<List<Data>> dataArray = [];
for (int rowIdx = 0; rowIdx < table.rows.length; rowIdx++) {
List<Data> rowData = [];
for (int colIdx = 0; colIdx < table.rows[rowIdx].length; colIdx++) {
var value = table.rows[rowIdx][colIdx]?.value;
rowData.add(Data(value, rowIdx, colIdx, sheet));
}
dataArray.add(rowData);
}
return dataArray;
}
List<List<Data>> decodeXLSData(Uint8List fileBytes) {
final Excel excelData = Excel.decodeBytes(fileBytes);
final sheet = excelData.tables.keys.first;
final table = excelData.tables[sheet]!;
List<List<Data>> dataArray = [];
for (int rowIdx = 0; rowIdx < table.rows.length; rowIdx++) {
List<Data> rowData = [];
for (int colIdx = 0; colIdx < table.rows[rowIdx].length; colIdx++) {
var value = table.rows[rowIdx][colIdx]?.value;
rowData.add(Data(value, rowIdx, colIdx, sheet));
}
dataArray.add(rowData);
}
return dataArray;
}
List<List<Data>> decodeCSVData(Uint8List fileBytes) {
String csvString = utf8.decode(fileBytes);
List<List<dynamic>> csvData = const CsvToListConverter().convert(csvString);
List<List<Data>> dataArray = [];
// Skip the header row (if it exists) and start from index 1
for (int i = 1; i < csvData.length; i++) {
List<Data> rowData = [];
for (int j = 0; j < csvData[i].length; j++) {
// Assuming the CSV data is of type String
rowData.add(Data(csvData[i][j].toString(), i, j, 'Sheet1'));
}
dataArray.add(rowData);
}
return dataArray;
}
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;
// }
logDebug(entry['Relation']);
if ((entry['Relation'].toString() == 'Son' ||
entry['Relation'].toString() == 'Daughter')) {
if (DateTime.now().difference(dob).inDays > 25 * 365) {
logDebug('child $dob');
invalidCount++;
}
} else if ((entry['Relation'] != 'Son' &&
entry['Relation'] != 'Daughter')) {
if (DateTime.now().difference(dob).inDays < 18 * 365) {
logDebug('others $dob');
invalidCount++;
}
}
}
return invalidCount;
}
void _dragAndDropFile(html.File file) async {
logDebug('file');
logDebug(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
logDebug(response.responseText);
}
void _retrieveAndUploadFile() {
final jsonString = html.window.localStorage['fileBytes'];
if (jsonString != null) {
final decodedBytes = json.decode(jsonString);
if (decodedBytes is List<dynamic>) {
setState(() {
fileName = fileName ??
'Retrieved File'; // Provide a default name if fileName is null
});
sendExcelFIleTOAPI(
Uint8List.fromList(decodedBytes.cast<int>()),
fileName!,
);
}
}
}
Future<void> sendExcelFIleTOAPI(Uint8List fileBytes, fileName) async {
// Future.delayed(Duration(seconds: 3), () {
// setState(() {
isLoading = true;
// });
// });
logDebug('submit');
logDebug(fileBytes);
if (fileBytes == null) {
logDebug('return');
return; // No file selected
} else {
logDebug('else');
// // Prepare form data
// final formData = html.FormData();
// formData.appendBlob('file', html.Blob([fileBytes]), fileName);
final enrollmentHrId = await tokenService.readValue('enrollmentHrId');
// URL of the API where you want to send the file
final apiUrl = Environment.apiUrl + 'employeeUpload';
logDebug('else');
// Create a multipart request
final request = http.MultipartRequest('POST', Uri.parse(apiUrl));
logDebug('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));
logDebug('Filename: $fileName');
request.files.add(http.MultipartFile.fromBytes(
'file',
fileBytes,
filename: fileName ?? 'default_filename.xlsx',
));
request.fields['client_id'] = localClientId;
// if (policyFirstPart == 'GPA') {
request.fields['policy_id'] = localClientPolicyId;
request.fields['client_branch_id'] = localClientBranchId;
request.fields['enrollment_open_date'] = openDateController.text;
request.fields['enrollment_close_date'] = closeDateController.text;
request.fields['created_by'] = enrollmentHrId!;
// } else {
// request.fields['policy_id'] = '3';
// }
logDebug('request : $request');
// Send the request
final response = await request.send();
logDebug('else');
// Read response stream as a string
final responseString = await response.stream.bytesToString();
logDebug('else');
// Check the status code of the response
if (response.statusCode == 200) {
isLoading = false;
Map<String, dynamic> data = json.decode(responseString);
if (data['status'] == false) {
ToastHelper.showErrorToast(context, data['message']);
logDebug('Table');
setState(() {
isSuccess = true;
successContent = data['message'];
excelValidationStaus = 0;
resetErrorCount();
handleImportAction();
getFileListDetails();
});
} else {
setState(() {
isLoading = false;
handleImportAction();
});
ToastHelper.showErrorToast2(context, "", data['message']);
setState(() {
resetErrorCount();
getFileListDetails();
});
// ToastHelper.showErrorToast(context, data['message']);
logDebug('Table');
}
} else {
setState(() {
isLoading = false;
});
// ToastHelper.showSuccessToast(
// context, 'Failed to upload file: ${response.reasonPhrase}');
ToastHelper.showErrorToast(context, 'Something went wrong');
logDebug('Failed to upload file: ${response.reasonPhrase}');
}
}
}
Future<void> handleImportAction() async {
logDebug('handleImportAction');
final postId = await tokenService.readValue('empHrId');
final preId = await tokenService.readValue('enrollmentEmpPrimaryId');
var activity = "import_enrollempdata";
dynamic response;
logDebug('postId - $postId');
logDebug('preId - $preId');
logDebug('activity - $activity');
try {
logDebug('10');
response = await apiService.getImportLogHrActivity(
postId!, preId!, localToken, activity);
if (response['status'] == 'success') {
logDebug('Request success');
} else {
// ToastHelper.showWarningToast(
// context, 'Request failed with status: ${response.statusCode}');
logDebug('Request failed with status: ${response['code']}');
}
} catch (e) {
logDebug('Exception occurred: $e');
}
}
Future<void> getFileListDetails() async {
final enrollmentPrimaryId =
await tokenService.readValue('enrollmentEmpPrimaryId');
final enrollmentClientId =
await tokenService.readValue('enrollmentClient_id');
logDebug('9');
try {
final response = await apiService.getFileListToApi(enrollmentPrimaryId,
localCardPolicyNo, enrollmentClientId, localToken, localTokenType);
if (response['status'] == true) {
logDebug('getThrFileList');
setState(() {
getThrFileList = List<Map<String, dynamic>>.from(response['data']);
originalData = getThrFileList;
filteredData = List.from(originalData);
logDebug('filteredData');
logDebug(filteredData);
});
} else {
logDebug('Request failed with status: ${response['code']}');
}
} catch (e) {
setState(() {
isLoading = false;
});
logDebug('Exception occurred: $e');
} finally {
setState(() {
// _isLoading = false;
});
}
}
void search(String query) {
setState(() {
if (query.isEmpty) {
// If search query is empty, show all data
filteredData = List.from(originalData);
} else {
// Filter the data based on the search query
filteredData = originalData.where((item) {
// Implement your filter logic here, for example:
return item['Emp_Code'].toLowerCase().contains(query.toLowerCase());
}).toList();
}
});
}
downloadSampleFile() {
final anchor = html.AnchorElement(href: 'assets/assets/Template_File.xlsx');
anchor.download = 'Template_File.xlsx'; // Set the filename
anchor.click(); // Trigger a click on the anchor element
}
// Future<void> downloadSampleFile() async {
//
// final response = await apiService.getSampleFileDownload(localToken);
// logDebug('check 1');
// if (response['status'] == 'success') {
// final url = response['data'];
// _launchURL(url);
// } else {
// ToastHelper.showErrorToast(context, '⚠️ Unknown response format');
// logDebug('⚠️ Unknown response format');
// }
// }
//
// Future<void> _launchURL(String url) async {
// logDebug('url $url');
// try {
// final Uri uri = Uri.parse(url);
// await launchUrl(uri, mode: LaunchMode.externalApplication);
// } catch (e) {
// logDebug('Could not launch URL: $e');
// }
// }
resetErrorCount() {
setState(() {
fileBytes = null;
fileName = null;
});
}
Future<void> getHrFileDownload(id, file_name) async {
// final http.Response response = await apiService.getHrFileDownloadToApi(id, localToken);
final apiurl = Environment.apiUrl;
final String url = '$apiurl/hrFileDownload?id=$id';
final token = localToken;
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 {
logDebug("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 {
logDebug("Download failed with status: ${response.statusCode}");
}
}
@override
Widget build(BuildContext context) {
return BaseLayout(
child: _buildContent(context),
);
}
Widget _buildContent(BuildContext context) {
return isLoading
? Container(
color: Colors.transparent, // 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(
// padding: const EdgeInsets.all(20),
// color: Color(0xFFEFF3F6),
child: Column(
children: [
Row(
children: [
IconButton(
tooltip: 'Previous Page',
onPressed: () async {
if (Navigator.canPop(context)) {
Navigator.pop(context);
return;
}
final clientId =
await tokenService.readValue('hr_ClientId') ?? '';
final policyTypeId =
await tokenService.readValue('hr_policyTypeId') ??
'';
final clientPolicyId =
await tokenService.readValue('hr_ClientPoliyId') ??
'';
final clientBranchId =
await tokenService.readValue('hr_clientBranchId') ??
'';
final token =
await tokenService.readValue('hr_Token') ?? '';
final tokenType =
await tokenService.readValue('hr_TokenType') ?? '';
final cardType =
await tokenService.readValue('hr_cardType') ?? '';
final policyNo =
await tokenService.readValue('hr_cardPolicyNo') ??
'';
final insurer = await tokenService
.readValue('hr_cardInsurer_name') ??
'';
final policyName = await tokenService
.readValue('hr_cardPolicy_name') ??
'';
final expDate = await tokenService
.readValue('hr_cardPolicy_ExpDate') ??
'';
final totalPremium =
await tokenService.readValue('hr_total_premium') ??
'';
final bulkDownload = await tokenService.readValue(
'hr_is_ecard_bulk_download_for_employee') ??
'0';
Navigator.pushReplacement(
context,
MaterialPageRoute(
settings:
const RouteSettings(name: 'hrPolicyDetails'),
builder: (_) => hrPolicyDetails(
ClientId: clientId,
policyTypeId: policyTypeId,
ClientPoliyId: clientPolicyId,
clientBranchId: clientBranchId,
Token: token,
TokenType: tokenType,
cardType: cardType,
cardPolicyNo: policyNo,
cardInsurer_name: insurer,
cardPolicy_name: policyName,
cardPolicy_ExpDate: expDate,
total_premium: totalPremium,
is_ecard_bulk_download_for_employee:
int.tryParse(bulkDownload) ?? 0,
),
),
);
},
icon: const Icon(
Icons.arrow_back_ios,
size: 18,
color: Colors.black,
),
),
const SizedBox(width: 6),
Container(
// color: Colors.redAccent.shade100,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: [
Text(
"${localCardType} - ${localCardPolicyNo} " ?? '',
style: GoogleFonts.poppins(
color: Colors.black,
fontSize: 14,
fontWeight: FontWeight.w500,
),
),
Text(
localTokenType == 'pre'
? "${localCardPolicyName} (${localCardPolicyExpDate})"
: "${localCardInsurerName} - ${localCardPolicyName} (${localCardPolicyExpDate})",
style: GoogleFonts.poppins(
color: Colors.grey,
fontSize: 12,
fontWeight: FontWeight.w400,
),
),
],
),
),
],
),
SizedBox(height: 20),
Expanded(
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
LayoutBuilder(
builder: (context, constraints) {
final narrow = constraints.maxWidth < 580;
if (narrow) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_dateField(
label: 'Enrolment Open Date',
controller: openDateController,
onTap: () async {
final picked = await showDatePicker(
context: context,
firstDate: DateTime(2000),
lastDate: DateTime.now(),
initialDate: DateTime.now(),
);
if (picked != null) {
final formatted =
DateFormat('dd-MM-yyyy')
.format(picked);
if (openDateController.text !=
formatted) {
closeDateController.clear();
}
openDateController.text = formatted;
}
},
),
const SizedBox(height: 16),
_dateField(
label: 'Enrolment Close Date',
controller: closeDateController,
onTap: () async {
if (openDateController.text.isEmpty) {
ToastHelper.showErrorToast(
context,
'Please select Enrolment Open Date first',
);
return;
}
final openDate =
DateFormat('dd-MM-yyyy').parse(
openDateController.text,
);
final picked = await showDatePicker(
context: context,
firstDate: openDate,
lastDate: DateTime(2100),
initialDate: openDate,
);
if (picked != null) {
closeDateController.text =
DateFormat('dd-MM-yyyy')
.format(picked);
}
},
),
],
);
}
return Row(
children: [
SizedBox(
width: 260,
child: _dateField(
label: 'Enrolment Open Date',
controller: openDateController,
onTap: () async {
final picked = await showDatePicker(
context: context,
firstDate: DateTime(2000),
lastDate: DateTime.now(),
initialDate: DateTime.now(),
);
if (picked != null) {
final formatted =
DateFormat('dd-MM-yyyy')
.format(picked);
if (openDateController.text !=
formatted) {
closeDateController.clear();
}
openDateController.text = formatted;
}
},
),
),
const SizedBox(width: 16),
SizedBox(
width: 260,
child: _dateField(
label: 'Enrolment Close Date',
controller: closeDateController,
onTap: () async {
if (openDateController.text.isEmpty) {
ToastHelper.showErrorToast(
context,
'Please select Enrolment Open Date first',
);
return;
}
final openDate =
DateFormat('dd-MM-yyyy').parse(
openDateController.text,
);
final picked = await showDatePicker(
context: context,
firstDate: openDate,
lastDate: DateTime(2100),
initialDate: openDate,
);
if (picked != null) {
closeDateController.text =
DateFormat('dd-MM-yyyy')
.format(picked);
}
},
),
),
],
);
},
),
SizedBox(height: 20),
Row(
children: [
Text(
'Upload File',
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w500,
color: Colors.black,
),
),
],
),
SizedBox(height: 5),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Expanded(
child: Container(
alignment: Alignment.center,
height: 125,
decoration: BoxDecoration(
color: Color(0xFFF7F5F6), // ✅ moved here
borderRadius: BorderRadius.circular(8),
border: Border.all(
color: const Color(0xFF00A6A6),
width: 1,
),
),
child: GestureDetector(
onTap: () {
if (!_validateDatesBeforeUpload()) return;
if (fileName == null) {
_uploadFile(
'Policy Name'); // ✅ same function
}
},
child: DragTarget<html.File>(
onAccept: (html.File droppedFile) {
if (!_validateDatesBeforeUpload()) return;
setState(() {
fileName = droppedFile.name;
});
_dragAndDropFile(droppedFile);
},
builder: (
BuildContext context,
List<html.File?> candidateData,
List<dynamic> rejectedData,
) {
return Container(
alignment: Alignment.center,
child: Column(
mainAxisAlignment:
MainAxisAlignment.center,
children: [
fileName != null
? Column(
mainAxisAlignment:
MainAxisAlignment
.center,
children: [
SizedBox(
width: 40,
height: 40,
child: Tooltip(
message:
'Upload', // The text that appears on hover
child: ElevatedButton(
onPressed: () =>
null,
style:
ElevatedButton
.styleFrom(
backgroundColor:
const Color(
0xFFD4F1F2),
elevation: 0,
padding: EdgeInsets
.zero, // ✅ IMPORTANT
alignment: Alignment
.center, // ✅ FORCE CENTER
shape:
RoundedRectangleBorder(
borderRadius:
BorderRadius
.circular(
10),
side:
const BorderSide(
// ✅ BORDER ADDED
color: Color(
0xFF00999E),
width: 1,
),
),
),
child: Icon(
Icons
.file_upload_outlined,
size: 22,
color: Color(
0xFF00999E),
)),
),
),
const SizedBox(
height: 15),
Text(
fileName!,
style: const TextStyle(
fontSize: 16),
),
const SizedBox(
height: 15),
MouseRegion(
cursor:
SystemMouseCursors
.click,
child: GestureDetector(
onTap:
resetErrorCount,
child: const Row(
mainAxisAlignment:
MainAxisAlignment
.center,
children: [
Icon(
Icons
.delete_forever,
size: 20,
color:
Colors.red,
),
SizedBox(
width: 4),
Text(
'Remove',
style:
TextStyle(
fontSize: 13,
color: Color(
0xFF727272),
),
),
],
),
),
),
],
)
: Column(
children: [
SizedBox(
width: 40,
height: 40,
child: Tooltip(
message:
'Upload', // The text that appears on hover
child: ElevatedButton(
onPressed: () {
if (!_validateDatesBeforeUpload())
return;
if (fileName ==
null) {
_uploadFile(
'Policy Name');
}
},
style:
ElevatedButton
.styleFrom(
backgroundColor:
const Color(
0xFFD4F1F2),
elevation: 0,
padding: EdgeInsets
.zero, // ✅ IMPORTANT
alignment: Alignment
.center, // ✅ FORCE CENTER
shape:
RoundedRectangleBorder(
borderRadius:
BorderRadius
.circular(
10),
side:
const BorderSide(
// ✅ BORDER ADDED
color: Color(
0xFF00999E),
width: 1,
),
),
),
child: Icon(
Icons
.file_upload_outlined,
size: 22,
color: Color(
0xFF00999E),
)),
),
),
SizedBox(height: 12),
Text(
'Upload Your Documents',
style:
GoogleFonts.poppins(
fontSize: 16,
fontWeight:
FontWeight
.w600,
color: Color(
0xFF000000)),
),
SizedBox(height: 8),
Text(
'(Supported Format: XLSX)',
style:
GoogleFonts.poppins(
fontSize: 10,
fontWeight:
FontWeight
.w400,
color: Color(
0xFF707070)),
),
],
),
],
),
);
},
),
),
),
),
],
),
SizedBox(height: 20),
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Expanded(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Text(
'Please download the sample file to review the format.',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w400,
color: Color(0xFF707070))),
MouseRegion(
cursor: SystemMouseCursors.click,
child: GestureDetector(
onTap: () {
downloadSampleFile();
},
child: Text(
'Template File',
style: TextStyle(
fontSize: 15,
color: Color(
0xFF00999E), // Add underline decoration
),
),
),
)
]))
],
),
SizedBox(height: 20),
Column(
children: [
_buildFileUploadedGrid(),
const SizedBox(height: 16),
_buildPagination(context),
],
),
],
),
),
),
],
),
);
}
Widget _buildFileUploadedGrid() {
if (filteredData.isEmpty) {
return const Center(child: Text('No uploaded files'));
}
return LayoutBuilder(
builder: (context, constraints) {
final width = constraints.maxWidth;
final crossAxisCount = width < 640 ? 1 : 2;
const mainAxisExtent = 88.0;
return GridView.builder(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
padding: const EdgeInsets.all(16),
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: crossAxisCount,
crossAxisSpacing: 16,
mainAxisSpacing: 16,
mainAxisExtent: mainAxisExtent,
),
itemCount: _paginatedData.length,
itemBuilder: (context, index) {
final item = _paginatedData[index];
return _buildFileCard(item);
},
);
},
);
}
// Widget _buildFileUploadedGrid() {
// if (filteredData.isEmpty) {
// return const SizedBox(
// height: 120,
// child: Center(child: Text('No uploaded files')),
// );
// }
//
// return GridView.builder(
// shrinkWrap: true,
// physics: const NeverScrollableScrollPhysics(),
// gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
// crossAxisCount: 2, // 👈 2 cards per row
// crossAxisSpacing: 16,
// mainAxisSpacing: 16,
// childAspectRatio: 10, // 👈 card height
// ),
// itemCount: _paginatedData.length,
// itemBuilder: (context, index) {
// final item = _paginatedData[index];
// return _buildFileCard(item);
// },
// );
// }
Widget _buildFileCard(Map<String, dynamic> item) {
return Container(
padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 10),
decoration: BoxDecoration(
color: const Color(0xFFEFF9FA),
borderRadius: BorderRadius.circular(12),
border: Border.all(color: const Color(0xFF9AD6DB)),
),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Container(
height: 40,
width: 40,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(10),
border: Border.all(color: const Color(0xFF00A6A6)),
),
child: const Icon(
Icons.description_outlined,
color: Color(0xFF00A6A6),
size: 20,
),
),
const SizedBox(width: 12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: [
Text(
item['file_name'] ?? '-',
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: GoogleFonts.poppins(
fontSize: 13,
fontWeight: FontWeight.w600,
color: const Color(0xFF101010),
),
),
const SizedBox(height: 2),
Text.rich(
TextSpan(
style: GoogleFonts.poppins(fontSize: 11),
children: [
TextSpan(
text: item['file_action'] ?? '',
style: const TextStyle(
color: Color(0xFF00999E),
fontWeight: FontWeight.w500,
),
),
const TextSpan(
text: ' - ',
style: TextStyle(color: Color(0xFF585858)),
),
TextSpan(
text: formatDate(item['created_at']),
style: const TextStyle(color: Color(0xFF585858)),
),
],
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
],
),
),
const SizedBox(width: 8),
Row(
mainAxisSize: MainAxisSize.min,
children: [
if (item['file_error_status'] == '1')
Tooltip(
message: 'Info',
child: InkWell(
onTap: () async {
final String? token =
await tokenService.getCurrentToken();
final String? enrollmentClient_id = await tokenService
.readValue('enrollmentClient_id');
final String? enrollmentEmpClientBranchId =
await tokenService
.readValue('enrollmentEmpClientBranchId');
if (token == null ||
enrollmentClient_id == null ||
enrollmentEmpClientBranchId == null) {
debugPrint(
'❌ Missing required data for navigation $token');
return;
}
if (!context.mounted) return;
await openExcelErrorScreenIfAvailable(
context: context,
apiService: apiService,
fileId: item['id'].toString(),
tokenType: 'pre',
clientId: enrollmentClient_id,
policyNo: item['policy_no']?.toString() ?? '',
action: item['file_action']?.toString() ?? '',
createdAt: item['created_at']?.toString() ?? '',
clientBranchId: enrollmentEmpClientBranchId,
token: token,
);
},
child: const Icon(
Icons.error,
size: 16,
color: Colors.red,
),
),
),
const SizedBox(width: 8),
_buildStatusChip(item['status']),
const SizedBox(width: 8),
Tooltip(
message: 'Download',
child: InkWell(
onTap: () {
getHrFileDownload(item['id'], item['file_name']);
},
child: Container(
height: 28,
width: 28,
decoration: BoxDecoration(
color: Color(0xFFC5F2F4),
borderRadius: BorderRadius.circular(10),
border: Border.all(color: const Color(0xFF76CED2)),
),
child: const Icon(
Icons.file_download_outlined,
color: Color(0xFF1D1B20),
size: 20,
),
),
),
),
],
),
],
),
);
}
Widget _buildStatusChip(String status) {
final s = status.toLowerCase();
Color bg;
if (s == 'success') {
bg = const Color(0xFF94E9B8);
} else if (s == 'failed') {
bg = const Color(0xFFFDC2C2);
} else if (s.contains('progress')) {
bg = const Color(0xFFFFE8AC);
} else {
bg = const Color(0xFFFBBF24);
}
return Container(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4),
decoration: BoxDecoration(
color: bg,
borderRadius: BorderRadius.circular(20),
),
child: Text(
status,
style: GoogleFonts.poppins(
fontSize: 11,
fontWeight: FontWeight.w500,
color: Colors.black,
),
),
);
}
Widget _buildPagination(BuildContext context) {
// 1. Calculate the range of entries being shown
final totalItems = filteredData.length;
final int startEntry =
totalItems == 0 ? 0 : ((_currentPage - 1) * _rowsPerPage) + 1;
int endEntry = _currentPage * _rowsPerPage;
if (endEntry > totalItems) endEntry = totalItems;
final totalPages = (filteredData.length / _rowsPerPage).ceil();
const visiblePageCount = 6;
List<int> getVisiblePages() {
if (totalPages <= visiblePageCount) {
return List.generate(totalPages, (i) => i + 1);
}
if (_currentPage <= 3) {
return [1, 2, 3, 4, 5];
}
if (_currentPage >= totalPages - 2) {
return [
totalPages - 4,
totalPages - 3,
totalPages - 2,
totalPages - 1,
totalPages
];
}
return [
_currentPage - 2,
_currentPage - 1,
_currentPage,
_currentPage + 1,
_currentPage + 2,
];
}
List<int> visiblePages = getVisiblePages();
Widget paginationControls = Row(
mainAxisSize: MainAxisSize.min,
children: [
DropdownButton<int>(
value: _rowsPerPage,
items: [6, 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;
});
},
),
IconButton(
onPressed:
_currentPage > 1 ? () => setState(() => _currentPage--) : null,
icon: const Icon(Icons.chevron_left),
),
if (!visiblePages.contains(1))
Row(
children: [
_buildPageButton(1),
const Padding(
padding: EdgeInsets.symmetric(horizontal: 4),
child: Text('...'),
),
],
),
for (int page in visiblePages) _buildPageButton(page),
if (!visiblePages.contains(totalPages) && totalPages > 0)
Row(
children: [
const Padding(
padding: EdgeInsets.symmetric(horizontal: 4),
child: Text('...'),
),
_buildPageButton(totalPages),
],
),
IconButton(
onPressed: _currentPage < totalPages
? () => setState(() => _currentPage++)
: null,
icon: const Icon(Icons.chevron_right),
),
],
);
final showingText = Text(
'Showing $startEntry to $endEntry of $totalItems entries',
style: GoogleFonts.poppins(
fontSize: 13,
color: const Color(0xFF585757),
fontWeight: FontWeight.w400,
),
);
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
child: LayoutBuilder(
builder: (context, constraints) {
final narrow = constraints.maxWidth < 720;
if (narrow) {
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
showingText,
const SizedBox(height: 8),
Align(
alignment: Alignment.centerRight,
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: paginationControls,
),
),
],
);
}
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
showingText,
Flexible(
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: paginationControls,
),
),
],
);
},
),
);
}
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 '-';
}
}
}
Widget _dateField({
required String label,
required TextEditingController controller,
required VoidCallback onTap,
}) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
label,
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w500,
color: Colors.black,
),
),
const SizedBox(height: 6),
SizedBox(
height: 38,
child: TextField(
controller: controller,
readOnly: true,
onTap: onTap,
style: GoogleFonts.poppins(fontSize: 13),
decoration: InputDecoration(
hintText: 'Select date',
hintStyle: const TextStyle(color: Color(0xFF9E9E9E)),
suffixIcon: const Icon(
Icons.calendar_month_outlined,
size: 18,
color: Color(0xFF00999E),
),
contentPadding:
const EdgeInsets.symmetric(horizontal: 12, vertical: 10),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(6),
borderSide: const BorderSide(
color: Color(0xFF00999E),
width: 1,
),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(6),
borderSide: const BorderSide(
color: Color(0xFF00999E),
width: 1.5,
),
),
),
),
),
],
);
}
class Data {
final dynamic value;
final int row;
final int column;
final String sheet;
Data(this.value, this.row, this.column, this.sheet);
}
class _DependenceDataSource0 extends DataTableSource {
final List<Map<String, dynamic>> _data;
_DependenceDataSource0(this._data);
@override
DataRow getRow(int index) {
final row = _data[index];
String dob = row['DOB'] != null ? formatDate(row['DOB']) : 'N/A';
String doj = row['DOJ'] != null ? formatDate(row['DOJ']) : 'N/A';
return DataRow(cells: [
DataCell(Text(row['Sno'].toString())),
DataCell(Text(row['Emp_Code'].toString())),
DataCell(Text(row['Name'].toString())),
DataCell(Text(doj)),
DataCell(Text(row['Gender']?.toString() ?? 'N/A')),
DataCell(Text(row['Relation']?.toString() ?? 'N/A')),
DataCell(Text(dob)),
DataCell(Text(row['Mail']?.toString() ?? 'N/A')),
DataCell(Text(row['Mobile']?.toString() ?? 'N/A')),
DataCell(Text(row['SI']?.toString() ?? 'N/A')),
DataCell(Text(row['Grade']?.toString() ?? 'N/A')),
]);
}
@override
bool get isRowCountApproximate => false;
@override
int get rowCount => _data.length;
@override
int get selectedRowCount => 0;
String formatDate(dynamic dateValue) {
if (dateValue is String) {
// If the date is already in string format
DateTime dateTime = DateTime.parse(dateValue);
return DateFormat('dd-MM-yyyy').format(dateTime);
} else if (dateValue is DateCellValue) {
// If dateValue is an instance of DateCellValue
return DateFormat('dd-MM-yyyy')
.format(DateTime.parse(dateValue.toString()));
} else {
// Handle other cases or null values
return 'N/A';
}
}
}