1534 lines
53 KiB
Dart
Executable File
1534 lines
53 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/presentation/excelVerification.dart';
|
|
import 'package:nhancepolicy/presentation/hrPolicyDetails.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 '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/base_layout.dart';
|
|
import '../customAppBar/customFooter.dart';
|
|
import 'package:nhancepolicy/logger.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;
|
|
final String total_premium;
|
|
final String allocgType;
|
|
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,
|
|
required this.total_premium,
|
|
this.allocgType = ''})
|
|
: super(key: key);
|
|
|
|
@override
|
|
State<postFileUpload> createState() => _postFileUploadState();
|
|
}
|
|
|
|
class _postFileUploadState extends State<postFileUpload> {
|
|
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 = '';
|
|
String localAllocgType = '';
|
|
|
|
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;
|
|
dynamic empClientId;
|
|
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;
|
|
final List<String> _allowedExtensions = ['xlsx', 'xls'];
|
|
|
|
bool showSampleButton = false;
|
|
String? currentApiValue; // To store the 'value' for the 2nd param
|
|
|
|
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);
|
|
}
|
|
|
|
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);
|
|
restoreUploadData().then((_) {
|
|
_loadToken();
|
|
getFileUploadMasterDetails();
|
|
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') ?? '';
|
|
|
|
localAllocgType = widget.allocgType;
|
|
}
|
|
|
|
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
|
|
// }
|
|
// }
|
|
|
|
Future<void> getFileUploadMasterDetails() async {
|
|
logDebug('9');
|
|
try {
|
|
final response = await apiService.getFileUploadMastersToApi(localToken,localAllocgType);
|
|
|
|
if (response['status'] == true) {
|
|
logDebug('getFileUploadMasterList1');
|
|
setState(() {
|
|
final actions = Map<String, String>.from(response['data']['actions']);
|
|
setState(() {
|
|
getFileUploadMasterList = actions.entries
|
|
.map((e) => {"key": e.key, "value": e.value})
|
|
.toList();
|
|
logDebug('getFileUploadMasterList: $getFileUploadMasterList');
|
|
});
|
|
logDebug('getFileUploadMasterList');
|
|
logDebug(getFileUploadMasterList);
|
|
});
|
|
} else {
|
|
logDebug('Request failed with status: ${response['code']}');
|
|
}
|
|
} catch (e) {
|
|
setState(() {
|
|
isLoading = false;
|
|
});
|
|
logDebug('Exception occurred: $e');
|
|
} finally {
|
|
setState(() {
|
|
// _isLoading = false;
|
|
});
|
|
}
|
|
}
|
|
|
|
Future<void> getFileListDetails() async {
|
|
empPrimaryId = await tokenService.readValue('empPrimaryId');
|
|
empClientId = await tokenService.readValue('empClientId');
|
|
|
|
logDebug('9');
|
|
try {
|
|
final response = await apiService.getFileListToApi(empPrimaryId,
|
|
localCardPolicyNo, empClientId, localToken, localTokenType);
|
|
|
|
if (response['status'] == 'success') {
|
|
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;
|
|
});
|
|
}
|
|
}
|
|
|
|
Future<void> getHrFileDownload(id, file_name) async {
|
|
// final http.Response response = await apiService.getHrFileDownloadToApi(id, localToken);
|
|
logDebug("**********-------*****");
|
|
final encryptClientId = localClientId;
|
|
logDebug(encryptClientId);
|
|
final apiurl = Environment.apiUrlPost;
|
|
final String url =
|
|
'$apiurl/hrFileDownload?id=$id&cliend_id=$encryptClientId';
|
|
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);
|
|
|
|
ToastHelper.showSuccessToast(context, 'File Downloaded Successfully');
|
|
} catch (e) {
|
|
throw Exception('Error parsing response: $e');
|
|
}
|
|
} else {
|
|
ToastHelper.showErrorToast(context, 'Failed to download');
|
|
logDebug("Download failed with status: ${response.statusCode}");
|
|
}
|
|
}
|
|
|
|
Future<void> downloadPostSampleFile(String apiParam) async {
|
|
logDebug("fun Sam f - in");
|
|
|
|
final apiurl = Environment.apiUrlPost;
|
|
final String url = '$apiurl/downloadSampleExcel/$apiParam';
|
|
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("fun sam f - ${response.statusCode}");
|
|
|
|
final apiContentType = response.headers['content-type'] ?? '';
|
|
final contentDisposition = response.headers['content-disposition'] ?? '';
|
|
final excelContentType =
|
|
apiContentType.contains('spreadsheetml') ||
|
|
apiContentType.contains('ms-excel')
|
|
? apiContentType
|
|
: 'application/vnd.ms-excel';
|
|
final utf8FileNameMatch = RegExp(
|
|
"filename\\*=UTF-8''([^;]+)",
|
|
caseSensitive: false,
|
|
).firstMatch(contentDisposition);
|
|
final plainFileNameMatch = RegExp(
|
|
'filename="?([^";]+)"?',
|
|
caseSensitive: false,
|
|
).firstMatch(contentDisposition);
|
|
final rawFileName =
|
|
utf8FileNameMatch?.group(1) ?? plainFileNameMatch?.group(1);
|
|
final fileName =
|
|
rawFileName != null && rawFileName.trim().isNotEmpty
|
|
? Uri.decodeComponent(rawFileName.trim())
|
|
: '${apiParam}_sample_file.xlsx';
|
|
|
|
// ✅ Create a blob from the response body bytes
|
|
final blob = html.Blob([response.bodyBytes], excelContentType);
|
|
|
|
// ✅ Generate a download URL
|
|
final url = html.Url.createObjectUrlFromBlob(blob);
|
|
|
|
// ✅ Trigger file download with API-provided filename
|
|
final anchor = html.AnchorElement(href: url);
|
|
anchor.setAttribute('download', fileName);
|
|
anchor.click();
|
|
|
|
// ✅ Revoke the URL to free memory
|
|
html.Url.revokeObjectUrl(url);
|
|
|
|
ToastHelper.showSuccessToast(context, 'File Downloaded Successfully');
|
|
} catch (e) {
|
|
logDebug("fun sam f - fail");
|
|
throw Exception('Error parsing response: $e');
|
|
}
|
|
} else {
|
|
ToastHelper.showErrorToast(context, 'Failed to download');
|
|
logDebug("Download failed with status: ${response.statusCode}");
|
|
}
|
|
}
|
|
|
|
// Future<void> downloadPostSampleFile(String apiParam) async {
|
|
// logDebug("fun Sam f - in");
|
|
//
|
|
// final apiurl = Environment.apiUrlPost;
|
|
// final String url = '$apiurl/downloadSampleExcel/$apiParam';
|
|
// 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("fun sam f - ${response.statusCode}");
|
|
//
|
|
// final apiContentType = response.headers['content-type'] ?? '';
|
|
// final excelContentType =
|
|
// apiContentType.contains('spreadsheetml') ||
|
|
// apiContentType.contains('ms-excel')
|
|
// ? apiContentType
|
|
// : 'application/vnd.ms-excel';
|
|
//
|
|
// // ✅ Create a blob from the response body bytes
|
|
// final blob = html.Blob([response.bodyBytes], excelContentType);
|
|
//
|
|
// // ✅ Generate a download URL
|
|
// final url = html.Url.createObjectUrlFromBlob(blob);
|
|
//
|
|
// // ✅ Trigger file download without assigning filename
|
|
// final anchor = html.AnchorElement(href: url);
|
|
// anchor.setAttribute('download', '');
|
|
// anchor.click();
|
|
//
|
|
// // ✅ Revoke the URL to free memory
|
|
// html.Url.revokeObjectUrl(url);
|
|
//
|
|
// ToastHelper.showSuccessToast(context, 'File Downloaded Successfully');
|
|
// } catch (e) {
|
|
// logDebug("fun sam f - fail");
|
|
// throw Exception('Error parsing response: $e');
|
|
// }
|
|
// } else {
|
|
// ToastHelper.showErrorToast(context, 'Failed to download');
|
|
// logDebug("Download failed with status: ${response.statusCode}");
|
|
// }
|
|
// }
|
|
|
|
void _uploadFile() 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',
|
|
);
|
|
|
|
setState(() {
|
|
fileName = null;
|
|
resetErrorCount();
|
|
// html.window.localStorage.remove('fileBytes');
|
|
});
|
|
|
|
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.');
|
|
}
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
Future<void> sendExcelFIleTOAPI(Uint8List fileBytes, fileName) async {
|
|
empPrimaryId = await tokenService.readValue('empPrimaryId');
|
|
// Future.delayed(Duration(seconds: 3), () {
|
|
setState(() {
|
|
isLoading = true;
|
|
});
|
|
// });
|
|
logDebug('submit');
|
|
logDebug(fileBytes);
|
|
if (fileBytes == null) {
|
|
ToastHelper.showErrorToast(context, 'Please upload file');
|
|
logDebug('return');
|
|
return; // No file selected
|
|
} else {
|
|
logDebug('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';
|
|
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.files.add(http.MultipartFile.fromBytes(
|
|
'file_name',
|
|
fileBytes,
|
|
filename: fileName ?? 'default_filename.xlsx',
|
|
));
|
|
logDebug('clintID: $clintID');
|
|
|
|
request.fields['client_id'] = localClientId;
|
|
request.fields['policy_no'] = localCardPolicyNo;
|
|
request.fields['client_branch_id'] = localClientBranchId;
|
|
request.fields['file_action'] = selectedKey!;
|
|
// request.fields['status'] = selectedKey!;
|
|
request.fields['created_by'] = empPrimaryId;
|
|
request.fields['policy_id'] = localClientPolicyId;
|
|
// "client_id": 1,
|
|
// "client_branch_id": 2,
|
|
// "policy_no": "POL123456",
|
|
// "file_action": ,
|
|
// "status": "inception",
|
|
// "created_by": 10
|
|
|
|
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(responseString);
|
|
Map<String, dynamic> data = json.decode(responseString);
|
|
if (data['status'] == true) {
|
|
logDebug('upload success');
|
|
setState(() {
|
|
isLoading = false;
|
|
});
|
|
ToastHelper.showSuccessToast(context, data['message']);
|
|
setState(() {
|
|
selectedValue = null;
|
|
selectedKey = null;
|
|
resetErrorCount();
|
|
getFileListDetails();
|
|
});
|
|
} else {
|
|
setState(() {
|
|
getFileListDetails();
|
|
isLoading = false;
|
|
selectedValue = null;
|
|
selectedKey = null;
|
|
resetErrorCount();
|
|
});
|
|
ToastHelper.showErrorToast(context, data['message']);
|
|
}
|
|
}
|
|
}
|
|
|
|
resetErrorCount() {
|
|
setState(() {
|
|
fileBytes = null;
|
|
fileName = null;
|
|
});
|
|
}
|
|
|
|
void search(String query) {
|
|
logDebug(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();
|
|
});
|
|
}
|
|
logDebug(filteredData.length);
|
|
}
|
|
|
|
@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(
|
|
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),
|
|
Expanded(
|
|
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),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
// Visibility toggles based on dropdown selection
|
|
Visibility(
|
|
visible: showSampleButton,
|
|
child: Padding(
|
|
padding: const EdgeInsets.only(left: 10),
|
|
child: SizedBox(
|
|
child: ElevatedButton(
|
|
onPressed: () {
|
|
// Pass the dynamic value to the function
|
|
downloadPostSampleFile(currentApiValue ?? '');
|
|
},
|
|
style: ElevatedButton.styleFrom(
|
|
backgroundColor: const Color(0xFFE26728),
|
|
elevation: 0,
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(10)),
|
|
),
|
|
child: Text(
|
|
'Sample Excel',
|
|
style: GoogleFonts.poppins(
|
|
fontSize: 14,
|
|
fontWeight: FontWeight.w700,
|
|
color: Colors.white),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
SizedBox(height: 20),
|
|
Expanded(
|
|
child: SingleChildScrollView(
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Row(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
/// Select File Action
|
|
Expanded(
|
|
flex: 5,
|
|
child: buildStyledDropdown(
|
|
label: 'Select File Action',
|
|
value: selectedKey,
|
|
items: getFileUploadMasterList,
|
|
onChanged: (val) {
|
|
setState(() {
|
|
selectedKey = val;
|
|
final selectedItem = getFileUploadMasterList
|
|
.firstWhere((e) => e['key'] == val);
|
|
selectedValue = selectedItem['value'];
|
|
currentApiValue = selectedItem['key'];
|
|
showSampleButton = true;
|
|
});
|
|
},
|
|
),
|
|
),
|
|
|
|
const SizedBox(width: 16),
|
|
|
|
/// Upload Box
|
|
Expanded(
|
|
flex: 5,
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
/// ✅ LABEL
|
|
RichText(
|
|
text: TextSpan(
|
|
text: 'Upload File',
|
|
style: GoogleFonts.poppins(
|
|
fontSize: 12,
|
|
fontWeight: FontWeight.w500,
|
|
color: Colors.black,
|
|
),
|
|
children: const [
|
|
TextSpan(
|
|
text: '(Supported Formats: XLSX)',
|
|
style: TextStyle(
|
|
fontSize: 11,
|
|
color: Colors.grey,
|
|
fontWeight: FontWeight.w400,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
|
|
const SizedBox(height: 6),
|
|
|
|
/// ✅ DOTTED UPLOAD BOX
|
|
DragTarget<html.File>(
|
|
onAccept: (html.File droppedFile) {
|
|
setState(() {
|
|
fileName = droppedFile.name;
|
|
});
|
|
_dragAndDropFile(droppedFile);
|
|
},
|
|
builder:
|
|
(context, candidateData, rejectedData) {
|
|
return GestureDetector(
|
|
onTap: () {
|
|
if (selectedValue != null) {
|
|
_uploadFile();
|
|
} else {
|
|
ToastHelper.showErrorToast(
|
|
context,
|
|
'Please select file action',
|
|
);
|
|
}
|
|
},
|
|
child: Container(
|
|
height: 40,
|
|
padding: const EdgeInsets.symmetric(
|
|
horizontal: 12),
|
|
decoration: BoxDecoration(
|
|
color: Colors.white,
|
|
borderRadius:
|
|
BorderRadius.circular(8),
|
|
border: Border.all(
|
|
color: const Color(0xFF00A6A6),
|
|
width: 1,
|
|
),
|
|
),
|
|
child: Row(
|
|
children: [
|
|
Expanded(
|
|
child: Text(
|
|
fileName ??
|
|
'Upload Your Documents',
|
|
overflow:
|
|
TextOverflow.ellipsis,
|
|
style: GoogleFonts.poppins(
|
|
fontSize: 13,
|
|
color: fileName == null
|
|
? Colors.grey
|
|
: Colors.black,
|
|
),
|
|
),
|
|
),
|
|
const Icon(
|
|
Icons.file_upload_outlined,
|
|
size: 18,
|
|
color: Colors.black,
|
|
),
|
|
],
|
|
),
|
|
));
|
|
},
|
|
),
|
|
],
|
|
),
|
|
),
|
|
],
|
|
),
|
|
SizedBox(height: 20),
|
|
Column(
|
|
children: [
|
|
_buildFileUploadedGrid(),
|
|
const SizedBox(height: 16),
|
|
_buildPagination(context),
|
|
],
|
|
),
|
|
])))
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget buildUploadBox({
|
|
required VoidCallback onTap,
|
|
}) {
|
|
return Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
'Upload File (Supported Formats: XLSX)',
|
|
style: GoogleFonts.poppins(
|
|
fontSize: 12,
|
|
fontWeight: FontWeight.w500,
|
|
color: Colors.black,
|
|
),
|
|
),
|
|
const SizedBox(height: 6),
|
|
GestureDetector(
|
|
onTap: onTap,
|
|
child: Container(
|
|
height: 42,
|
|
padding: const EdgeInsets.symmetric(horizontal: 12),
|
|
decoration: BoxDecoration(
|
|
color: Colors.white,
|
|
borderRadius: BorderRadius.circular(8),
|
|
border: Border.all(
|
|
color: const Color(0xFF00A6A6),
|
|
style: BorderStyle.solid,
|
|
),
|
|
),
|
|
child: Row(
|
|
children: [
|
|
Expanded(
|
|
child: Text(
|
|
'Upload Your Documents',
|
|
style: GoogleFonts.poppins(
|
|
fontSize: 13,
|
|
color: Colors.grey[600],
|
|
),
|
|
),
|
|
),
|
|
const Icon(
|
|
Icons.upload_file,
|
|
size: 18,
|
|
color: Color(0xFF00A6A6),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
|
|
Widget buildStyledDropdown({
|
|
required String label,
|
|
required String? value,
|
|
required List<Map<String, dynamic>> items,
|
|
required Function(String?) onChanged,
|
|
}) {
|
|
return Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
label,
|
|
style: GoogleFonts.poppins(
|
|
fontSize: 12,
|
|
fontWeight: FontWeight.w500,
|
|
color: Colors.black,
|
|
),
|
|
),
|
|
const SizedBox(height: 6),
|
|
Container(
|
|
height: 42,
|
|
padding: const EdgeInsets.symmetric(horizontal: 12),
|
|
decoration: BoxDecoration(
|
|
color: Colors.white,
|
|
borderRadius: BorderRadius.circular(8),
|
|
// border: Border.all(color: const Color(0xFFE0E0E0)),
|
|
),
|
|
child: DropdownButtonHideUnderline(
|
|
child: DropdownButton<String>(
|
|
isExpanded: true,
|
|
value: value,
|
|
hint: Text(
|
|
'Select',
|
|
style: GoogleFonts.poppins(fontSize: 13),
|
|
),
|
|
icon: const Icon(Icons.keyboard_arrow_down),
|
|
items: items.map((item) {
|
|
return DropdownMenuItem<String>(
|
|
value: item['key'],
|
|
child: Text(
|
|
item['value'],
|
|
style: GoogleFonts.poppins(fontSize: 13),
|
|
),
|
|
);
|
|
}).toList(),
|
|
onChanged: onChanged,
|
|
),
|
|
),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
|
|
Widget _buildFileUploadedGrid() {
|
|
if (filteredData.isEmpty) {
|
|
return const SizedBox(
|
|
height: 120,
|
|
child: Center(child: Text('No uploaded files')),
|
|
);
|
|
}
|
|
|
|
return GridView.builder(
|
|
shrinkWrap: true, // ✅ IMPORTANT
|
|
physics: const NeverScrollableScrollPhysics(), // ✅ Disable inner scroll
|
|
padding: const EdgeInsets.all(16),
|
|
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
|
|
crossAxisCount: 2,
|
|
crossAxisSpacing: 16,
|
|
mainAxisSpacing: 16,
|
|
childAspectRatio: 10,
|
|
),
|
|
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: 12),
|
|
decoration: BoxDecoration(
|
|
color: const Color(0xFFEFF9FA),
|
|
borderRadius: BorderRadius.circular(12),
|
|
border: Border.all(color: const Color(0xFF9AD6DB)),
|
|
),
|
|
child: Row(
|
|
crossAxisAlignment: CrossAxisAlignment.center,
|
|
children: [
|
|
/// 📄 File Icon
|
|
Container(
|
|
height: 44,
|
|
width: 44,
|
|
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: 22,
|
|
),
|
|
),
|
|
|
|
const SizedBox(width: 12),
|
|
|
|
/// 📑 LEFT CONTENT
|
|
Expanded(
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
/// Row 1 → File name
|
|
Text(
|
|
item['file_name'] ?? '-',
|
|
maxLines: 1,
|
|
overflow: TextOverflow.ellipsis,
|
|
style: GoogleFonts.poppins(
|
|
fontSize: 13,
|
|
fontWeight: FontWeight.w600,
|
|
color: const Color(0xFF101010),
|
|
),
|
|
),
|
|
|
|
const SizedBox(height: 4),
|
|
|
|
/// Row 2 → Action - Date
|
|
RichText(
|
|
text: 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)),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
|
|
/// 👉 RIGHT SIDE (ICON + STATUS + DOWNLOAD)
|
|
Column(
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
crossAxisAlignment: CrossAxisAlignment.end,
|
|
children: [
|
|
/// 🔴 Error + Status
|
|
Row(
|
|
children: [],
|
|
),
|
|
|
|
const SizedBox(height: 8),
|
|
|
|
Row(
|
|
children: [
|
|
if (item['file_error_status'] == '1')
|
|
InkWell(
|
|
onTap: () async {
|
|
logDebug(item);
|
|
// return;
|
|
final String? token =
|
|
await tokenService.getCurrentToken();
|
|
final String? empClientId =
|
|
await tokenService.readValue('empClientId');
|
|
final String? empBranchId =
|
|
await tokenService.readValue('empClientBranchId');
|
|
|
|
logDebug(item);
|
|
logDebug(empClientId);
|
|
logDebug(localPolicyTypeId);
|
|
logDebug(empBranchId);
|
|
logDebug(token);
|
|
logDebug('post');
|
|
logDebug(localCardType);
|
|
logDebug(localCardPolicyNo);
|
|
logDebug(localCardInsurerName);
|
|
logDebug(localCardPolicyName);
|
|
logDebug(localCardPolicyExpDate);
|
|
logDebug(item['id']);
|
|
|
|
// ✅ SAFETY CHECK
|
|
if (token == null ||
|
|
empClientId == null ||
|
|
empBranchId == null) {
|
|
debugPrint(
|
|
'❌ Missing required data for navigation ${token}');
|
|
return;
|
|
}
|
|
|
|
Navigator.push(
|
|
context,
|
|
MaterialPageRoute(
|
|
builder: (context) => excelErrorScreen(
|
|
ClientId: empClientId,
|
|
policy_no: item['policy_no'],
|
|
action: item['file_action'],
|
|
created_at: item['created_at'],
|
|
clientBranchId: empBranchId,
|
|
Token: token,
|
|
TokenType: 'post',
|
|
id: item['id']),
|
|
),
|
|
);
|
|
},
|
|
child: Icon(
|
|
Icons.error,
|
|
size: 16,
|
|
color: Colors.red,
|
|
),
|
|
),
|
|
SizedBox(width: 10),
|
|
_buildStatusChip(item['status']),
|
|
SizedBox(width: 10),
|
|
InkWell(
|
|
onTap: () {
|
|
getHrFileDownload(item['id'], item['file_name']);
|
|
},
|
|
child: Container(
|
|
height: 30,
|
|
width: 30,
|
|
decoration: BoxDecoration(
|
|
color: Color(0xFFC5F2F4),
|
|
borderRadius: BorderRadius.circular(10),
|
|
border: Border.all(color: const Color(0xFF76CED2)),
|
|
),
|
|
child: Icon(
|
|
Icons.file_download_outlined,
|
|
color: Color(0xFF1D1B20),
|
|
size: 22,
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
|
|
/// ⬇ Download
|
|
],
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
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,
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
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 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();
|
|
|
|
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: [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;
|
|
});
|
|
},
|
|
),
|
|
|
|
// Previous button
|
|
IconButton(
|
|
tooltip: 'Previous Page',
|
|
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 '-';
|
|
}
|
|
}
|
|
}
|