This commit is contained in:
Surendiran 2026-03-17 10:55:37 +05:30
parent ad396fefb0
commit 72b253918a
17 changed files with 3949 additions and 2169 deletions

View File

@ -220,7 +220,7 @@ class _NhanceSideBarState extends State<NhanceSideBar> {
);
}).toList(),
if(postModules.isNotEmpty)
if(postModules.isNotEmpty && postModules.contains(5))
_SideItem(
// icon: Icons.dashboard,
icon: SvgPicture.string(

View File

@ -69,6 +69,7 @@ class _MyEmailVerifyState extends State<MyEmailVerify> {
late String _verificationId;
dynamic empMobileNo;
dynamic empEmailid;
final tokenService = TokenStorageService();
@override
void initState() {
@ -225,12 +226,53 @@ class _MyEmailVerifyState extends State<MyEmailVerify> {
// // Show a Snackbar if the OTP is invalid
// print('Invalid OTP. Please try again');
// }
} else if (response.statusCode == 401) {
setState(() {
_isLoading = false;
});
await tokenService.clearAll(); // 🔐 clears flutter_secure_storage
ToastHelper.showErrorToast(context, 'Session Out');
if (!context.mounted) return;
Navigator.pushNamedAndRemoveUntil(
context,
'hrLogin',
(route) => false,
);
} else if (response.statusCode == 403) {
setState(() {
_isLoading = false;
});
await tokenService.clearAll(); // 🔐 clears flutter_secure_storage
ToastHelper.showErrorToast(context, 'Session Out');
if (!context.mounted) return;
Navigator.pushNamedAndRemoveUntil(
context,
'hrLogin',
(route) => false,
);
} else if (response.statusCode == 451) {
setState(() {
_isLoading = false;
});
final body = jsonDecode(response.body);
final message = body['message'];
ToastHelper.showWarningToast(context, message);
} else if (response.statusCode == 429) {
setState(() {
_isLoading = false;
});
final body = jsonDecode(response.body);
final message = body['message'];
ToastHelper.showWarningToast(context, message);
} else {
setState(() {
_isLoading = false;
});
ToastHelper.showWarningToast(context, 'Something went wrong');
throw Exception('Failed to verify OTP');
throw Exception('Failed to load data');
}
} catch (e) {
setState(() {

View File

@ -1,6 +1,7 @@
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:http/http.dart' as http;
@ -12,6 +13,7 @@ import 'package:nhancepolicy/responsive.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:flutter_animated_button/flutter_animated_button.dart';
import 'package:nhancepolicy/service/token_storage_service.dart';
import 'package:url_launcher/url_launcher.dart';
import 'config/environment.dart';
import 'email_verify.dart';
@ -517,48 +519,106 @@ class _MyPhoneState extends State<MyHrLogin> {
),
SizedBox(height: 20),
Container(
width: double
.infinity, // Make the footer full width
child: Container(
alignment: Alignment.bottomCenter,
padding:
EdgeInsets.symmetric(vertical: 8),
child: RichText(
textAlign: TextAlign.center,
text: TextSpan(
text:
'By continuing, you agree with our ',
style: GoogleFonts.poppins(
color: Colors.black,
fontSize: 9,
),
children: <TextSpan>[
TextSpan(
text: 'privacy policy ',
style: GoogleFonts.poppins(
color: Color(0xFFE26828),
fontSize: 9,
),
),
TextSpan(
text: 'and ',
style: GoogleFonts.poppins(
color: Colors.black,
fontSize: 9,
),
),
TextSpan(
text: 'terms of use',
style: GoogleFonts.poppins(
color: Color(0xFFE26828),
fontSize: 9,
),
),
],
width: double.infinity,
alignment: Alignment.bottomCenter,
padding: const EdgeInsets.symmetric(vertical: 8),
child: RichText(
textAlign: TextAlign.center,
text: TextSpan(
text: 'By continuing, you agree with our ',
style: GoogleFonts.poppins(
color: Colors.black,
fontSize: 9,
),
children: [
TextSpan(
text: 'privacy policy ',
style: GoogleFonts.poppins(
color: const Color(0xFFE26828),
fontSize: 9,
// decoration: TextDecoration.underline,
),
recognizer: TapGestureRecognizer()
..onTap = () async {
final url = Uri.parse(
'https://nhanceindia.in/privacy-policy/');
if (await canLaunchUrl(url)) {
await launchUrl(url,
mode: LaunchMode.externalApplication);
}
},
),
TextSpan(
text: 'and ',
style: GoogleFonts.poppins(
color: Colors.black,
fontSize: 9,
),
),
TextSpan(
text: 'terms of use',
style: GoogleFonts.poppins(
color: const Color(0xFFE26828),
fontSize: 9,
// decoration: TextDecoration.underline,
),
recognizer: TapGestureRecognizer()
..onTap = () async {
final url = Uri.parse(
'https://nhanceindia.in/privacy-policy/');
if (await canLaunchUrl(url)) {
await launchUrl(url,
mode: LaunchMode.externalApplication);
}
},
),
],
),
),
),
// Container(
// width: double
// .infinity, // Make the footer full width
// child: Container(
// alignment: Alignment.bottomCenter,
// padding:
// EdgeInsets.symmetric(vertical: 8),
// child: RichText(
// textAlign: TextAlign.center,
// text: TextSpan(
// text:
// 'By continuing, you agree with our ',
// style: GoogleFonts.poppins(
// color: Colors.black,
// fontSize: 9,
// ),
// children: <TextSpan>[
// TextSpan(
// text: 'privacy policy ',
// style: GoogleFonts.poppins(
// color: Color(0xFFE26828),
// fontSize: 9,
// ),
// ),
// TextSpan(
// text: 'and ',
// style: GoogleFonts.poppins(
// color: Colors.black,
// fontSize: 9,
// ),
// ),
// TextSpan(
// text: 'terms of use',
// style: GoogleFonts.poppins(
// color: Color(0xFFE26828),
// fontSize: 9,
// ),
// ),
// ],
// ),
// ),
// ),
// ),
],
),
),

View File

@ -62,117 +62,282 @@ Future<void> startApp() async {
projectId: 'nhance-ee8d1'));
// await dotenv.load(fileName: Environment.fileName);
runApp(MaterialApp(
title: 'Nhance HR',
onGenerateTitle: (context) => "Nhance HR",
initialRoute: 'hrLogin',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primaryColor: Color(0xFF00999E), // Primary theme color
scaffoldBackgroundColor: Colors.white,
colorScheme: ColorScheme.fromSeed(
seedColor: Color(0xFF00999E),
),
textTheme: GoogleFonts.poppinsTextTheme(),
elevatedButtonTheme: ElevatedButtonThemeData(
style: ElevatedButton.styleFrom(
backgroundColor: Color(0xFF00999E), // Button background
// runApp(MaterialApp(
// title: 'Nhance HR',
// onGenerateTitle: (context) => "Nhance HR",
// initialRoute: 'hrLogin',
// debugShowCheckedModeBanner: false,
// theme: ThemeData(
// primaryColor: Color(0xFF00999E), // Primary theme color
// scaffoldBackgroundColor: Colors.white,
// colorScheme: ColorScheme.fromSeed(
// seedColor: Color(0xFF00999E),
// ),
// textTheme: GoogleFonts.poppinsTextTheme(),
// elevatedButtonTheme: ElevatedButtonThemeData(
// style: ElevatedButton.styleFrom(
// backgroundColor: Color(0xFF00999E), // Button background
// ),
// ),
// ),
// routes: {
// 'phone': (context) => MyPhone(),
// 'mailVerify': (context) => MyEmailVerify(
// type: '',
// value: '',
//
// ),
// 'verify': (context) => MyVerify(
// verificationId: '',
// mobileNumber: '',
// resendToken: null,
// onResendCode: (String, int) {},
// ),
// 'home': (context) => MyApp(),
// 'hrLogin': (context) => MyHrLogin(),
// // 'hrVerify': (context) => MyHrVerify(
// // verificationId: '',
// // mobileNumber: '',
// // resendToken: null,
// // onResendCode: (String, int) {},
// // ),
// 'hrHome': (context) => MyHrHome(),
// 'preFileUpload': (context) => const preFileUpload(
// ClientId: '',
// policyTypeId: '',
// ClientPoliyId: '',
// clientBranchId: '',
// Token: '',
// TokenType: '',
// cardType: '',
// cardPolicyNo: '',
// cardInsurer_name: '',
// cardPolicy_name: '',
// cardPolicy_ExpDate: '',
// total_premium: '',
// ),
// 'postFileUpload': (context) => const postFileUpload(
// ClientId: '',
// policyTypeId: '',
// ClientPoliyId: '',
// clientBranchId: '',
// Token: '',
// TokenType: '',
// cardType: '',
// cardPolicyNo: '',
// cardInsurer_name: '',
// cardPolicy_name: '',
// cardPolicy_ExpDate: '',
// total_premium: '',
// ),
// 'excelErrorScreen': (context) => const excelErrorScreen(
// ClientId: '',
// policy_no: '',
// action: '',
// created_at: '',
// clientBranchId: '',
// Token: '',
// TokenType: '',
// id: ''
// ),
// 'empDetails': (context) => empDetails(),
// 'addOnsDetails': (context) => addOnsDetails(),
// 'empReviewDetails': (context) => empReviewDetails(),
// 'hrDashboard': (context) => hrDashboard(),
// 'hrPolicyDetails': (context) => hrPolicyDetails(
// ClientId: '',
// policyTypeId: '',
// ClientPoliyId: '',
// clientBranchId: '',
// Token: '',
// TokenType: '',
// cardType: '',
// cardPolicyNo: '',
// cardInsurer_name: '',
// cardPolicy_name: '',
// cardPolicy_ExpDate: '',
// total_premium: '',
// is_ecard_bulk_download_for_employee: 0,
// ),
// 'oldPolicy': (context) => oldPolicy(),
// 'branchSelection': (context) => BranchSelectionPage(),
// 'policies': (context) => policies(),
// 'CdPoliciesList': (context) => CdPoliciesList(),
// 'ClaimsPolicies': (context) => ClaimsPolicies(
// empCode:'',
// ),
// 'cdTransactionDetails': (context) => cdTransactionDetails(
// insurerName: '',
// cdMasterAccountNo: '',
// insurerId: '',
// cd_ac_pk: '',
// empClientId: '',
// ),
// },
// ));
final tokenService = TokenStorageService();
final token = await tokenService.getCurrentToken();
runApp(MyApp(initialToken: token));
}
class MyApp extends StatelessWidget {
final String? initialToken;
const MyApp({super.key, this.initialToken});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Nhance HR',
debugShowCheckedModeBanner: false,
initialRoute: (initialToken == null || initialToken!.isEmpty)
? 'hrLogin'
: 'hrHome',
theme: ThemeData(
primaryColor: const Color(0xFF00999E),
scaffoldBackgroundColor: Colors.white,
colorScheme: ColorScheme.fromSeed(
seedColor: const Color(0xFF00999E),
),
textTheme: GoogleFonts.poppinsTextTheme(),
elevatedButtonTheme: ElevatedButtonThemeData(
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFF00999E),
),
),
),
),
routes: {
'phone': (context) => MyPhone(),
'mailVerify': (context) => MyEmailVerify(
type: '',
value: '',
),
'verify': (context) => MyVerify(
verificationId: '',
mobileNumber: '',
resendToken: null,
onResendCode: (String, int) {},
),
'home': (context) => MyApp(),
'hrLogin': (context) => MyHrLogin(),
// 'hrVerify': (context) => MyHrVerify(
// verificationId: '',
// mobileNumber: '',
// resendToken: null,
// onResendCode: (String, int) {},
// ),
'hrHome': (context) => MyHrHome(),
'preFileUpload': (context) => const preFileUpload(
ClientId: '',
policyTypeId: '',
ClientPoliyId: '',
clientBranchId: '',
Token: '',
TokenType: '',
cardType: '',
cardPolicyNo: '',
cardInsurer_name: '',
cardPolicy_name: '',
cardPolicy_ExpDate: '',
total_premium: '',
),
'postFileUpload': (context) => const postFileUpload(
ClientId: '',
policyTypeId: '',
ClientPoliyId: '',
clientBranchId: '',
Token: '',
TokenType: '',
cardType: '',
cardPolicyNo: '',
cardInsurer_name: '',
cardPolicy_name: '',
cardPolicy_ExpDate: '',
total_premium: '',
),
'excelErrorScreen': (context) => const excelErrorScreen(
ClientId: '',
policy_no: '',
action: '',
created_at: '',
clientBranchId: '',
Token: '',
TokenType: '',
id: ''
),
'empDetails': (context) => empDetails(),
'addOnsDetails': (context) => addOnsDetails(),
'empReviewDetails': (context) => empReviewDetails(),
'hrDashboard': (context) => hrDashboard(),
'hrPolicyDetails': (context) => hrPolicyDetails(
ClientId: '',
policyTypeId: '',
ClientPoliyId: '',
clientBranchId: '',
Token: '',
TokenType: '',
cardType: '',
cardPolicyNo: '',
cardInsurer_name: '',
cardPolicy_name: '',
cardPolicy_ExpDate: '',
total_premium: '',
is_ecard_bulk_download_for_employee: 0,
),
'oldPolicy': (context) => oldPolicy(),
'branchSelection': (context) => BranchSelectionPage(),
'policies': (context) => policies(),
'CdPoliciesList': (context) => CdPoliciesList(),
'ClaimsPolicies': (context) => ClaimsPolicies(
empCode:'',
),
'cdTransactionDetails': (context) => cdTransactionDetails(
insurerName: '',
cdMasterAccountNo: '',
insurerId: '',
cd_ac_pk: '',
empClientId: '',
),
},
));
routes: appRoutes,
);
}
}
final Map<String, WidgetBuilder> appRoutes = {
'phone': (context) => MyPhone(),
'mailVerify': (context) => MyEmailVerify(
type: '',
value: '',
),
'verify': (context) => MyVerify(
verificationId: '',
mobileNumber: '',
resendToken: null,
onResendCode: (String, int) {},
),
'home': (context) => MyApp(),
'hrLogin': (context) => MyHrLogin(),
// 'hrVerify': (context) => MyHrVerify(
// verificationId: '',
// mobileNumber: '',
// resendToken: null,
// onResendCode: (String, int) {},
// ),
'hrHome': (context) => MyHrHome(),
'preFileUpload': (context) => const preFileUpload(
ClientId: '',
policyTypeId: '',
ClientPoliyId: '',
clientBranchId: '',
Token: '',
TokenType: '',
cardType: '',
cardPolicyNo: '',
cardInsurer_name: '',
cardPolicy_name: '',
cardPolicy_ExpDate: '',
total_premium: '',
),
'postFileUpload': (context) => const postFileUpload(
ClientId: '',
policyTypeId: '',
ClientPoliyId: '',
clientBranchId: '',
Token: '',
TokenType: '',
cardType: '',
cardPolicyNo: '',
cardInsurer_name: '',
cardPolicy_name: '',
cardPolicy_ExpDate: '',
total_premium: '',
),
'excelErrorScreen': (context) => const excelErrorScreen(
ClientId: '',
policy_no: '',
action: '',
created_at: '',
clientBranchId: '',
Token: '',
TokenType: '',
id: ''
),
'empDetails': (context) => empDetails(),
'addOnsDetails': (context) => addOnsDetails(),
'empReviewDetails': (context) => empReviewDetails(),
'hrDashboard': (context) => hrDashboard(),
'hrPolicyDetails': (context) => hrPolicyDetails(
ClientId: '',
policyTypeId: '',
ClientPoliyId: '',
clientBranchId: '',
Token: '',
TokenType: '',
cardType: '',
cardPolicyNo: '',
cardInsurer_name: '',
cardPolicy_name: '',
cardPolicy_ExpDate: '',
total_premium: '',
is_ecard_bulk_download_for_employee: 0,
),
'oldPolicy': (context) => oldPolicy(),
'branchSelection': (context) => BranchSelectionPage(),
'policies': (context) => policies(),
'CdPoliciesList': (context) => CdPoliciesList(),
'ClaimsPolicies': (context) => ClaimsPolicies(
empCode:'',
),
'cdTransactionDetails': (context) => cdTransactionDetails(
insurerName: '',
cdMasterAccountNo: '',
insurerId: '',
cd_ac_pk: '',
empClientId: '',
),
};
/// 🔐 Global Auth Wrapper (Protects All Pages)
class AuthWrapper extends StatefulWidget {
final Widget child;
const AuthWrapper({super.key, required this.child});
@override
State<AuthWrapper> createState() => _AuthWrapperState();
}
class _AuthWrapperState extends State<AuthWrapper> {
@override
void initState() {
super.initState();
_checkLogin();
}
void _checkLogin() {
final token = TokenStorageService().getCurrentToken();
if (token == null || token.isEmpty) {
WidgetsBinding.instance.addPostFrameCallback((_) {
Navigator.pushNamedAndRemoveUntil(
context, 'hrLogin', (route) => false);
});
}
}
@override
Widget build(BuildContext context) {
return widget.child;
}
}

File diff suppressed because it is too large Load Diff

View File

@ -73,11 +73,12 @@ class _CdPoliciesListState extends State<CdPoliciesList> {
}
Future<void> checkIds() async {
print('checkIds');
_postPreToken = await tokenService.getCurrentToken();
empClientId = await tokenService.readValue('empClientId');
// empClientBranchId = await tokenService.readValue('empClientBranchId');
empHrId = await tokenService.readValue('empHrId');
print('$_postPreToken - $empClientId - $empHrId');
await getCDPoliciesDetails(empClientId, empHrId, _postPreToken);
}
@ -165,7 +166,7 @@ class _CdPoliciesListState extends State<CdPoliciesList> {
rows.add([
item['insurer_name'] ?? '',
item['cd_master_account_no'] ?? '',
'${item['balance'] ?? '0'}',
'${item['balance'] ?? '0'}',
]);
}
@ -393,6 +394,30 @@ class _CdPoliciesListState extends State<CdPoliciesList> {
return InkWell(
borderRadius: BorderRadius.circular(12),
onTap: () async {
await tokenService.writeValue(
'hr_cd_insurer_name',
filteredData[index]['insurer_name'].toString(),
);
await tokenService.writeValue(
'hr_cd_master_account_no',
filteredData[index]['cd_master_account_no'].toString(),
);
await tokenService.writeValue(
'hr_cd_insurer_id',
filteredData[index]['insurer_id'].toString(),
);
await tokenService.writeValue(
'hr_cd_ac_pk',
filteredData[index]['cd_ac_pk'].toString(),
);
await tokenService.writeValue(
'hr_empClientId',
empClientId.toString(),
);
Navigator.push(
context,
@ -462,7 +487,8 @@ class _CDPolicyCard extends StatelessWidget {
Expanded(
flex: 3,
child: Text(
"${balance.toStringAsFixed(0)}",
data['balance'] ?? '',
// "${balance.toStringAsFixed(0)}",
textAlign: TextAlign.right,
style: GoogleFonts.poppins(
fontSize: 14,

View File

@ -1,7 +1,6 @@
import 'dart:convert';
import 'package:csv/csv.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
@ -13,12 +12,12 @@ 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:collection/collection.dart';
import 'package:url_launcher/url_launcher.dart';
import '../config/environment.dart';
import '../customAppBar/base_layout.dart';
import '../customAppBar/customAppBar.dart';
import '../customAppBar/customFooter.dart';
import 'cdList.dart';
import 'claims.dart';
class cdTransactionDetails extends StatefulWidget {
final String insurerName;
@ -42,6 +41,14 @@ class cdTransactionDetails extends StatefulWidget {
}
class _cdTransactionDetailsState extends State<cdTransactionDetails> {
String? localInsurerId;
String? localCdAcPk;
String? localEmpClientId;
String? localInsurerName;
String? localCdMasterAccountNo;
final tokenService = TokenStorageService();
Uint8List? fileBytes;
List<Map<String, dynamic>> getCDTransData = [];
@ -85,7 +92,9 @@ class _cdTransactionDetailsState extends State<cdTransactionDetails> {
void initState() {
super.initState();
apiService = ApiService(context); // Initialize ApiService here
getCdTransactionDetails();
restoreTransactionData();
}
@override
@ -93,9 +102,51 @@ class _cdTransactionDetailsState extends State<cdTransactionDetails> {
super.dispose();
}
// downloadPolicyFiles?file_id=13
// getPolicyAndEndorsementFiles?cd_ac_pk=12
Future<void> restoreTransactionData() async {
localInsurerId = widget.insurerId.trim().isNotEmpty
? widget.insurerId
: await tokenService.readValue('hr_cd_insurer_id');
localCdAcPk = widget.cd_ac_pk.trim().isNotEmpty
? widget.cd_ac_pk
: await tokenService.readValue('hr_cd_ac_pk');
localEmpClientId = widget.empClientId.trim().isNotEmpty
? widget.empClientId
: await tokenService.readValue('hr_empClientId');
localInsurerName = widget.insurerName.trim().isNotEmpty
? widget.insurerName
: await tokenService.readValue('hr_cd_insurer_name');
localCdMasterAccountNo = widget.cdMasterAccountNo.trim().isNotEmpty
? widget.cdMasterAccountNo
: await tokenService.readValue('hr_cd_master_account_no');
print('restore localInsurerId = $localInsurerId');
print('restore localCdAcPk = $localCdAcPk');
print('restore localEmpClientId = $localEmpClientId');
if (localInsurerId != null &&
localCdAcPk != null &&
localEmpClientId != null) {
getCdTransactionDetails();
}
}
Future<void> clearPolicyStorage() async {
await tokenService.removeValue('hr_cd_insurer_id');
await tokenService.removeValue('hr_cd_ac_pk');
await tokenService.removeValue('hr_empClientId');
await tokenService.removeValue('hr_cd_insurer_name');
await tokenService.removeValue('hr_cd_master_account_no');
}
Future<void> getCdTransactionDetails() async {
print('9');
setState(() {
@ -104,8 +155,8 @@ class _cdTransactionDetailsState extends State<cdTransactionDetails> {
try {
print('10');
final _postPreToken = await tokenService.getCurrentToken();
final response = await apiService.getCdTransactionData(widget.empClientId,
widget.insurerId, widget.cd_ac_pk, _postPreToken!);
final response = await apiService.getCdTransactionData(localEmpClientId!,
localInsurerId!, localCdAcPk!, _postPreToken!);
if (response['status'] == 'success') {
setState(() {
isLoading = false;
@ -115,11 +166,16 @@ class _cdTransactionDetailsState extends State<cdTransactionDetails> {
List<Map<String, dynamic>>.from(response['data']['deposit_data']);
originalData = getCDTransData;
filteredData = List.from(originalData);
total_deposit = formatAmount(response['data']['total_deposit']);
total_consumed = formatAmount(response['data']['total_consumed']);
total_refund = formatAmount(response['data']['total_refund']);
currect_balance = formatAmount(response['data']['currect_balance']);
insurer_short_name = formatAmount(response['data']['insurer_short_name']);
total_deposit = response['data']['total_deposit'];
// total_deposit = formatAmount(response['data']['total_deposit']);
total_consumed = response['data']['total_consumed'];
// total_consumed = formatAmount(response['data']['total_consumed']);
total_refund = response['data']['total_refund'];
// total_refund = formatAmount(response['data']['total_refund']);
currect_balance = response['data']['currect_balance'];
// currect_balance = formatAmount(response['data']['currect_balance']);
insurer_short_name = response['data']['insurer_short_name'];
// insurer_short_name = formatAmount(response['data']['insurer_short_name']);
print('filteredData');
print(filteredData);
});
@ -144,25 +200,53 @@ class _cdTransactionDetailsState extends State<cdTransactionDetails> {
}
}
Future<void> _openEndorsementFile(id) async {
print('9');
try {
print('10');
final _postPreToken = await tokenService.getCurrentToken();
final response = await apiService.getOpenEndorsementFileData(id, _postPreToken!);
if (response['status'] == false) {
ToastHelper.showErrorToast(context, response['message']);
} else {
Future<void> _openEndorsementFile(id, file_name) async {
// final http.Response response = await apiService.getHrFileDownloadToApi(id, widget.Token);
final _postPreToken = await tokenService.getCurrentToken();
print("**********-------*****");
final apiurl = Environment.apiUrlPost;
final String url = '$apiurl/downloadPolicyFiles?file_id=$id';
// ToastHelper.showWarningToast(
// context, 'Request failed with status: ${response.statusCode}');
print('Request failed with status: ${response['code']}');
final response = await http.get(
Uri.parse(url),
headers: {
'APP-SIGNATURE':
'nhance-2025-signature-T8f6gV9k5fK2wf5V5c4vId3b0J8z6cAm2x8Y',
'Authorization': 'Bearer $_postPreToken',
'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);
ToastHelper.showSuccessToast(context, 'File Downloaded Successfully');
} catch (e) {
throw Exception('Error parsing response: $e');
}
} catch (e) {
print('Exception occurred: $e');
} else {
ToastHelper.showErrorToast(context, 'Failed to download');
print("Download failed with status: ${response.statusCode}");
}
}
Future<void> getCdEndorsementDetails(id) async {
print('9');
setState(() {
@ -243,7 +327,7 @@ class _cdTransactionDetailsState extends State<cdTransactionDetails> {
),
onTap: () {
// Navigator.pop(context); // close popup
_openEndorsementFile(file['id']);
_openEndorsementFile(file['id'],file['file_name']);
},
);
},
@ -387,9 +471,12 @@ class _cdTransactionDetailsState extends State<cdTransactionDetails> {
: '-',
item['endorsement_no'] ?? '',
item['sub_type_text'] ?? '',
item['transaction_type'] == 'Credit' ? '${formatAmount(item['amount'])}' : '-',
item['transaction_type'] == 'Debit' ? '${formatAmount(item['amount'])}' : '-',
'${formatAmount(item['balance']) ?? '0'}',
item['transaction_type'] == 'Credit' ? '${item['amount']}' : '-',
// item['transaction_type'] == 'Credit' ? '${formatAmount(item['amount'])}' : '-',
item['transaction_type'] == 'Debit' ? '${item['amount']}' : '-',
// item['transaction_type'] == 'Debit' ? '${formatAmount(item['amount'])}' : '-',
'${item['balance'] ?? '0'}',
// '${formatAmount(item['balance']) ?? '0'}',
item['description'] ?? '',
item['username'] ?? '',
]);
@ -541,7 +628,21 @@ class _cdTransactionDetailsState extends State<cdTransactionDetails> {
children: [
IconButton(
tooltip: 'Previous Page',
onPressed: () => Navigator.pop(context),
onPressed: () async {
await clearPolicyStorage();
if (Navigator.canPop(context)) {
Navigator.pop(context);
} else {
Navigator.pushReplacement(
context,
MaterialPageRoute(
settings: const RouteSettings(name: 'cdPoliciesList'),
builder: (_) => CdPoliciesList(),
),
);
}
},
icon: const Icon(
Icons.arrow_back_ios,
size: 18,
@ -552,7 +653,7 @@ class _cdTransactionDetailsState extends State<cdTransactionDetails> {
),
const SizedBox(width: 6),
Text(
'Transaction Details - ${insurer_short_name} (${widget.cdMasterAccountNo})',
'Transaction Details - ${insurer_short_name} (${localCdMasterAccountNo ?? widget.cdMasterAccountNo})',
style: GoogleFonts.poppins(
fontSize: 18,
fontWeight: FontWeight.w500,
@ -762,21 +863,36 @@ class _cdTransactionDetailsState extends State<cdTransactionDetails> {
SizedBox(width: 10),
_cell(
item['transaction_type'] == 'Credit'
? formatAmount(item['amount'])
? item['amount']
: '-',
2,
alignRight: true,
),
// _cell(
// item['transaction_type'] == 'Credit'
// ? formatAmount(item['amount'])
// : '-',
// 2,
// alignRight: true,
// ),
SizedBox(width: 10),
_cell(
item['transaction_type'] == 'Debit'
? formatAmount(item['amount'])
? item['amount']
: '-',
2,
alignRight: true,
),
// _cell(
// item['transaction_type'] == 'Debit'
// ? formatAmount(item['amount'])
// : '-',
// 2,
// alignRight: true,
// ),
SizedBox(width: 10),
_cell(formatAmount(item['balance']), 2, alignRight: true),
_cell(item['balance'], 2, alignRight: true),
// _cell(formatAmount(item['balance']), 2, alignRight: true),
SizedBox(width: 10),
_cell(item['description'], 3),
SizedBox(width: 10),
@ -784,25 +900,72 @@ class _cdTransactionDetailsState extends State<cdTransactionDetails> {
SizedBox(width: 10),
Expanded(
flex: 2,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
if (isAllowedSubType)
_ActionIconButton(
icon: Icons.picture_as_pdf_outlined,
toolTip: 'View Endorsement PDF',
onTap: () => getCdEndorsementDetails(item['id']),
child: SizedBox(
height: 36,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
/// --- PDF ICON SLOT ---
SizedBox(
width: 36,
height: 36,
child: Visibility(
visible: isAllowedSubType,
maintainSize: true,
maintainAnimation: true,
maintainState: true,
child: _ActionIconButton(
icon: Icons.picture_as_pdf_outlined,
toolTip: 'View Endorsement PDF',
onTap: () => getCdEndorsementDetails(item['id']),
),
),
),
const SizedBox(width: 8),
if (isAllowedSubType && hasSplitUpFile)
_ActionIconButton(
icon: Icons.folder_open_outlined,
toolTip: 'View Files',
onTap: () => _launchURL(item['split_up_url']),
const SizedBox(width: 8),
/// --- FOLDER ICON SLOT ---
SizedBox(
width: 36,
height: 36,
child: Visibility(
visible: isAllowedSubType && hasSplitUpFile,
maintainSize: true,
maintainAnimation: true,
maintainState: true,
child: _ActionIconButton(
icon: Icons.folder_open_outlined,
toolTip: 'View Files',
onTap: () => _launchURL(item['split_up_url']),
),
),
),
],
],
),
),
),
// Expanded(
// flex: 2,
// child: Row(
// mainAxisAlignment: MainAxisAlignment.center,
// children: [
// if (isAllowedSubType)
// _ActionIconButton(
// icon: Icons.picture_as_pdf_outlined,
// toolTip: 'View Endorsement PDF',
// onTap: () => getCdEndorsementDetails(item['id']),
// ),
// const SizedBox(width: 8),
// if (isAllowedSubType && hasSplitUpFile)
// _ActionIconButton(
// icon: Icons.folder_open_outlined,
// toolTip: 'View Files',
// onTap: () => _launchURL(item['split_up_url']),
// ),
// ],
// ),
// ),
],
),
);

View File

@ -66,6 +66,8 @@ class _ClaimsPolicieState extends State<ClaimsPolicies> {
return filteredData.sublist(startIndex, endIndex);
}
int? appliedClaimStatus;
Color getStatusColor(String status) {
switch (status.toLowerCase()) {
case 'claim received':
@ -182,7 +184,7 @@ class _ClaimsPolicieState extends State<ClaimsPolicies> {
});
try {
print('10');
final response = await apiService.getClaimPoliciesToApi(_postPreToken!);
final response = await apiService.getClaimPoliciesToApi(_postPreToken!,'');
if (response['status'] == 'success') {
setState(() {
isLoading = false;
@ -272,6 +274,9 @@ class _ClaimsPolicieState extends State<ClaimsPolicies> {
final data = claim_Detials();
print("data -- $data");
getClaimList();
setState(() {
appliedClaimStatus = selectedClaimStatus; // apply only after API call
});
}
Future<void> reset() async {
@ -285,6 +290,7 @@ class _ClaimsPolicieState extends State<ClaimsPolicies> {
selectedClaimStatus = null;
selectedClaimStatusName = null;
_currentPage = 1;
appliedClaimStatus = null;
getClaimList();
});
@ -482,16 +488,19 @@ class _ClaimsPolicieState extends State<ClaimsPolicies> {
/// 🔙 Back + Title (LEFT)
Row(
children: [
// IconButton(
// onPressed: () => {},
// icon: const Icon(
// Icons.arrow_back_ios,
// size: 18,
// color: Colors.black,
// ),
// padding: EdgeInsets.zero,
// constraints: const BoxConstraints(),
// ),
if(widget.empCode.isNotEmpty)
IconButton(
onPressed: () => {
Navigator.of(context).pop(),
},
icon: const Icon(
Icons.arrow_back_ios,
size: 18,
color: Colors.black,
),
padding: EdgeInsets.zero,
constraints: const BoxConstraints(),
),
const SizedBox(width: 6),
Text(
'Claims',
@ -707,11 +716,6 @@ class _ClaimsPolicieState extends State<ClaimsPolicies> {
);
}
Widget _buildDataRow(Map<String, dynamic> item) {
return Container(
padding: const EdgeInsets.symmetric(vertical: 6, horizontal: 16),
@ -866,21 +870,28 @@ class _ClaimsPolicieState extends State<ClaimsPolicies> {
if (statusMaster.isEmpty) return const SizedBox();
final List<dynamic> displayStatuses = appliedClaimStatus != null
? statusMaster.where((status) =>
int.parse(status['id'].toString()) == appliedClaimStatus)
.toList()
: statusMaster;
return SizedBox(
height: 30,
height: 35,
child: ListView.separated(
scrollDirection: Axis.horizontal,
itemCount: statusMaster.length,
itemCount: displayStatuses.length,
separatorBuilder: (_, __) => const SizedBox(width: 10),
itemBuilder: (context, index) {
final statusName =
statusMaster[index]['claim_status']?.toString() ?? '';
displayStatuses[index]['claim_status']?.toString() ?? '';
final count = getStatusCount(statusName);
final color = getStatusColor(statusName);
return Container(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 8),
padding:
const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
decoration: BoxDecoration(
color: color,
borderRadius: BorderRadius.circular(6),

View File

@ -34,6 +34,9 @@ class _hrDashboardState extends State<hrDashboard> with SingleTickerProviderStat
final tokenService = TokenStorageService();
final FocusNode _policyFocusNode = FocusNode();
bool isTpaDashboardEnabled = false;
bool isTpaSelected = false;
// Variables for API data
dynamic empClientBranchId;
dynamic empHrId;
@ -41,6 +44,7 @@ class _hrDashboardState extends State<hrDashboard> with SingleTickerProviderStat
String? _postPreToken = '';
int stausVal = 1;
bool _isPolicyDropdownOpen = false;
html.IFrameElement? _currentIframe;
@override
void initState() {
@ -114,17 +118,32 @@ class _hrDashboardState extends State<hrDashboard> with SingleTickerProviderStat
_metabaseLoaded = false;
});
// REMOVE OLD IFRAME COMPLETELY
_currentIframe?.remove();
_currentIframe = null;
// CLEAR OLD VIEW TYPES
_registeredViewTypes.clear();
final response = await apiService.postHrDashboard({
"client_id": empClientId,
"client_policy_id": clientPolicyId,
}, _postPreToken);
if (response['status'] == 'success') {
// ADD THIS LINE
isTpaDashboardEnabled = response['is_tpa_dashboard_enable'] == true;
setState(() {
isTpaSelected = false;
});
_registerMetabaseIframe(
token: response['data']['metabaseToken'],
url: response['data']['metabaseUrl'],
clientPolicyId: clientPolicyId,
);
setState(() => _metabaseLoaded = true);
} else {
ToastHelper.showErrorToast(context, response['message']);
@ -136,26 +155,74 @@ class _hrDashboardState extends State<hrDashboard> with SingleTickerProviderStat
}
}
Future<void> _loadTpaDashboard(String clientPolicyId) async {
try {
setState(() {
isDashboardLoading = true;
_metabaseLoaded = false;
});
// REMOVE OLD IFRAME
_currentIframe?.remove();
_currentIframe = null;
_registeredViewTypes.clear();
final response = await apiService.postHrTpaDashboard({
"client_id": empClientId,
"client_policy_id": clientPolicyId,
}, _postPreToken);
if (response['status'] == 'success') {
setState(() {
isTpaSelected = true;
});
_registerMetabaseIframe(
token: response['data']['metabaseToken'],
url: response['data']['metabaseUrl'],
clientPolicyId: clientPolicyId,
);
setState(() => _metabaseLoaded = true);
} else {
ToastHelper.showErrorToast(context, response['message']);
}
} catch (e) {
ToastHelper.showErrorToast(context, 'TPA Dashboard loading failed');
} finally {
setState(() => isDashboardLoading = false);
}
}
void _registerMetabaseIframe({
required String token,
required String url,
required String clientPolicyId,
}) {
final viewType = 'metabase-dashboard-$clientPolicyId';
// 🔥 ALWAYS CREATE UNIQUE VIEW TYPE
final viewType =
'metabase-dashboard-${clientPolicyId}-${DateTime.now().millisecondsSinceEpoch}';
_dashboardViewType = viewType;
if (_registeredViewTypes.contains(viewType)) return;
final embedUrl =
"$url/embed/dashboard/$token"
"#theme=light&bordered=true&titled=true"
"&v=${DateTime.now().millisecondsSinceEpoch}"; // 🔥 cache buster
final embedUrl = "$url/embed/dashboard/$token#theme=light&bordered=true&titled=true";
final iframe = html.IFrameElement()
..src = embedUrl
..style.border = 'none'
..style.width = '100%'
..style.height = '100%'
..allowFullscreen = true;
_currentIframe = iframe;
ui.platformViewRegistry.registerViewFactory(
viewType,
(int viewId) => html.IFrameElement()
..src = embedUrl
..style.border = 'none'
..style.width = '100%'
..style.height = '100%'
..allowFullscreen = true,
(int viewId) => iframe,
);
_registeredViewTypes.add(viewType);
@ -185,7 +252,7 @@ class _hrDashboardState extends State<hrDashboard> with SingleTickerProviderStat
Widget _buildDashboardView() {
if (postModules.isNotEmpty && activePoliciesList.isEmpty) {
return const Center(child: Text('No dashboard data available for your account'));
return const Center(child: Text('No active policy found.'));
}
return Padding(
@ -339,95 +406,127 @@ class _hrDashboardState extends State<hrDashboard> with SingleTickerProviderStat
color: Colors.white,
child: Row(
children: [
const Text(
'Select Policy',
style: TextStyle(fontWeight: FontWeight.w600, fontSize: 13),
),
const SizedBox(width: 12),
SizedBox(
width: 420,
height: 40,
child: SearchAnchor(
viewBackgroundColor: Colors.white,
viewConstraints: const BoxConstraints(maxHeight: 220),
builder: (BuildContext context, SearchController controller) {
String displayText = "Select Policy";
if (selectedPolicyId != null) {
final policy = activePoliciesList.firstWhere(
(p) => p['client_policy_id'].toString() == selectedPolicyId,
orElse: () => {},
);
if (policy.isNotEmpty) {
displayText = "${policy['type']} - ${policy['policy_no']}";
}
}
return InkWell(
onTap: () {
setState(() => _isPolicyDropdownOpen = true); // 🔥 OPEN
controller.openView();
},
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 12),
decoration: BoxDecoration(
border: Border.all(color: Colors.grey.shade300),
borderRadius: BorderRadius.circular(8),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: Text(
displayText,
style: const TextStyle(fontSize: 12),
overflow: TextOverflow.ellipsis,
),
),
const Icon(Icons.arrow_drop_down, color: Colors.grey),
],
),
),
);
},
suggestionsBuilder:
(BuildContext context, SearchController controller) {
final input = controller.text.toLowerCase();
return activePoliciesList
.where((policy) =>
policy['type']
.toString()
.toLowerCase()
.contains(input) ||
policy['policy_no']
.toString()
.toLowerCase()
.contains(input))
.map((policy) {
final label =
"${policy['type']} - ${policy['policy_no']}";
return ListTile(
dense: true,
title: Text(label, style: const TextStyle(fontSize: 13)),
onTap: () {
setState(() {
selectedPolicyId =
policy['client_policy_id'].toString();
_isPolicyDropdownOpen = false; // 🔥 CLOSE
});
controller.closeView(label);
_loadDashboardByPolicy(selectedPolicyId!);
},
);
}).toList();
},
if (!isTpaSelected)...[
const Text(
'Select Policy',
style: TextStyle(fontWeight: FontWeight.w600, fontSize: 13),
),
const SizedBox(width: 12),
SizedBox(
width: 420,
height: 40,
child: SearchAnchor(
viewBackgroundColor: Colors.white,
viewConstraints: const BoxConstraints(maxHeight: 220),
builder: (BuildContext context, SearchController controller) {
String displayText = "Select Policy";
if (selectedPolicyId != null) {
final policy = activePoliciesList.firstWhere(
(p) => p['client_policy_id'].toString() == selectedPolicyId,
orElse: () => {},
);
if (policy.isNotEmpty) {
displayText = "${policy['type']} - ${policy['policy_no']}";
}
}
return InkWell(
onTap: () {
setState(() => _isPolicyDropdownOpen = true); // 🔥 OPEN
controller.openView();
},
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 12),
decoration: BoxDecoration(
border: Border.all(color: Colors.grey.shade300),
borderRadius: BorderRadius.circular(8),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: Text(
displayText,
style: const TextStyle(fontSize: 12),
overflow: TextOverflow.ellipsis,
),
),
const Icon(Icons.arrow_drop_down, color: Colors.grey),
],
),
),
);
},
suggestionsBuilder:
(BuildContext context, SearchController controller) {
final input = controller.text.toLowerCase();
return activePoliciesList
.where((policy) =>
policy['type']
.toString()
.toLowerCase()
.contains(input) ||
policy['policy_no']
.toString()
.toLowerCase()
.contains(input))
.map((policy) {
final label =
"${policy['type']} - ${policy['policy_no']}";
return ListTile(
dense: true,
title: Text(label, style: const TextStyle(fontSize: 13)),
onTap: () {
setState(() {
selectedPolicyId =
policy['client_policy_id'].toString();
_isPolicyDropdownOpen = false; // 🔥 CLOSE
});
controller.closeView(label);
_loadDashboardByPolicy(selectedPolicyId!);
},
);
}).toList();
},
),
),
],
const Spacer(),
if (isTpaDashboardEnabled)
ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: isTpaSelected
? Colors.teal
: Colors.grey.shade300,
foregroundColor:
isTpaSelected ? Colors.white : Colors.black,
),
onPressed: () {
if (selectedPolicyId == null) return;
if (isTpaSelected) {
// 🔄 Switch BACK to Normal Dashboard
_loadDashboardByPolicy(selectedPolicyId!);
} else {
// 🔄 Switch TO TPA Dashboard
_loadTpaDashboard(selectedPolicyId!);
}
},
child: Text(
isTpaSelected
? "Insights from Nhance"
: "Insights from TPA ",
),
),
),
],
),
);

File diff suppressed because it is too large Load Diff

View File

@ -106,43 +106,114 @@ class _policiesState extends State<policies>
}
}
// Future<void> _loadToken() async {
// final enrollmentRaw = await tokenService.readValue('enrollmentAllowed_modules'); // "[1]"
// final postRaw = await tokenService.readValue('empAllowed_modules'); // "[2,3,4]"
//
// // Decode safely
// enrollmentModules = enrollmentRaw != null && enrollmentRaw.isNotEmpty
// ? List<int>.from(jsonDecode(enrollmentRaw))
// : [];
//
// postModules = postRaw != null && postRaw.isNotEmpty
// ? List<int>.from(jsonDecode(postRaw))
// : [];
//
// print('enrollmentModules $enrollmentModules');
// print('postModules $postModules');
//
// _postPreToken = await tokenService.getCurrentToken();
// print(_postPreToken);
//
// if (enrollmentModules.contains(1)) {
// enrollmentClient_id = await tokenService.readValue('enrollmentClient_id');
// enrollmentEmpClientBranchId =
// await tokenService.readValue('enrollmentEmpClientBranchId');
// enrollmentHrId = await tokenService.readValue('enrollmentHrId');
//
// await getPreCashDepositDetails(enrollmentEmpClientBranchId,
// enrollmentClient_id, enrollmentHrId, _postPreToken);
// }
//
// if (postModules.contains(2)) {
// empClientId = await tokenService.readValue('empClientId');
// empClientBranchId = await tokenService.readValue('empClientBranchId');
// empHrId = await tokenService.readValue('empHrId');
//
// await getPostCashDepositDetails(empClientBranchId, empClientId, empHrId, _postPreToken);
// }
//
// }
Future<void> _loadToken() async {
final enrollmentRaw = await tokenService.readValue('enrollmentAllowed_modules'); // "[1]"
final postRaw = await tokenService.readValue('empAllowed_modules'); // "[2,3,4]"
setState(() {
isLoading = true; // 🔥 START LOADER HERE
});
// Decode safely
enrollmentModules = enrollmentRaw != null && enrollmentRaw.isNotEmpty
? List<int>.from(jsonDecode(enrollmentRaw))
: [];
try {
final enrollmentRaw =
await tokenService.readValue('enrollmentAllowed_modules');
final postRaw =
await tokenService.readValue('empAllowed_modules');
postModules = postRaw != null && postRaw.isNotEmpty
? List<int>.from(jsonDecode(postRaw))
: [];
enrollmentModules = enrollmentRaw != null && enrollmentRaw.isNotEmpty
? List<int>.from(jsonDecode(enrollmentRaw))
: [];
print('enrollmentModules $enrollmentModules');
print('postModules $postModules');
postModules = postRaw != null && postRaw.isNotEmpty
? List<int>.from(jsonDecode(postRaw))
: [];
_postPreToken = await tokenService.getCurrentToken();
print(_postPreToken);
_postPreToken = await tokenService.getCurrentToken();
if (enrollmentModules.contains(1)) {
enrollmentClient_id = await tokenService.readValue('enrollmentClient_id');
enrollmentEmpClientBranchId =
await tokenService.readValue('enrollmentEmpClientBranchId');
enrollmentHrId = await tokenService.readValue('enrollmentHrId');
List<Future> apiCalls = [];
await getPreCashDepositDetails(enrollmentEmpClientBranchId,
enrollmentClient_id, enrollmentHrId, _postPreToken);
/// 👇 Add APIs dynamically
if (enrollmentModules.contains(1)) {
enrollmentClient_id =
await tokenService.readValue('enrollmentClient_id');
enrollmentEmpClientBranchId =
await tokenService.readValue('enrollmentEmpClientBranchId');
enrollmentHrId =
await tokenService.readValue('enrollmentHrId');
apiCalls.add(
getPreCashDepositDetails(
enrollmentEmpClientBranchId,
enrollmentClient_id,
enrollmentHrId,
_postPreToken,
),
);
}
if (postModules.contains(2)) {
empClientId = await tokenService.readValue('empClientId');
empClientBranchId =
await tokenService.readValue('empClientBranchId');
empHrId = await tokenService.readValue('empHrId');
apiCalls.add(
getPostCashDepositDetails(
empClientBranchId,
empClientId,
empHrId,
_postPreToken,
),
);
}
/// 🔥 WAIT FOR ALL APIs
await Future.wait(apiCalls);
} catch (e) {
print("Error in _loadToken: $e");
} finally {
if (mounted) {
setState(() {
isLoading = false; // 🔥 STOP LOADER ONLY ONCE
});
}
}
if (postModules.contains(2)) {
empClientId = await tokenService.readValue('empClientId');
empClientBranchId = await tokenService.readValue('empClientBranchId');
empHrId = await tokenService.readValue('empHrId');
await getPostCashDepositDetails(empClientBranchId, empClientId, empHrId, _postPreToken);
}
}
Future<void> getPreCashDepositDetails(enrollmentEmpClientBranchId,
@ -153,7 +224,6 @@ class _policiesState extends State<policies>
print("hr_id -$enrollmentHrId");
print("token -$_postPreToken");
isLoading = true;
// setState(() {
// _isLoading = true;
// });
@ -171,7 +241,6 @@ class _policiesState extends State<policies>
// clintID!, clintBranchId!, hr_id, token);
print('IN1');
if (response['status'] == 'success') {
isLoading = false;
setState(() {
print('response');
print(response['data']);
@ -198,7 +267,6 @@ class _policiesState extends State<policies>
print("hr_id -$empHrId");
print("token -$_postPreToken");
isLoading = true;
// setState(() {
// _isLoading = true;
// });
@ -213,7 +281,6 @@ class _policiesState extends State<policies>
// clintID!, clintBranchId!, hr_id, token);
print('IN1');
if (response['status'] == 'success') {
isLoading = false;
setState(() {
print('response');
print(response['data']);
@ -226,7 +293,6 @@ class _policiesState extends State<policies>
print('IN2');
} else {
isLoading = false;
print('API request failed with status');
setState(() {
activePoliciesList = [];
@ -345,7 +411,16 @@ class _policiesState extends State<policies>
required List<Map<String, dynamic>> openEnrollment,
required List<Map<String, dynamic>> activePolicies,
}) {
return Scaffold(
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
),
) : Scaffold(
body: SingleChildScrollView(
// padding: const EdgeInsets.all(20),
@ -385,39 +460,45 @@ class _policiesState extends State<policies>
SizedBox(height: 15),
Container(
width: double.infinity,
height: 400,
// constraints: const BoxConstraints(
// minHeight: 180,
// ),
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(6),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Open for Enrollment',
style: GoogleFonts.poppins(fontSize: 14, fontWeight: FontWeight.w600),
),
SizedBox(height: 14),
child: IntrinsicHeight(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Open for Enrollment',
style: GoogleFonts.poppins(fontSize: 14, fontWeight: FontWeight.w600),
),
SizedBox(height: 14),
/// SCROLLABLE AREA
openEnrollment.isEmpty
? _EmptyBox('No policies open for enrollment')
: _PolicyGrid(
policies: openEnrollment,
isEnrollment: true,
),
],
),
)
/// SCROLLABLE AREA
Expanded(
child: openEnrollment.isEmpty
? _EmptyBox('No policies open for enrollment')
: _PolicyGrid(
policies: openEnrollment,
isEnrollment: true,
),
),
],
),
),
],
if(postModules.contains(2))...[
SizedBox(height: 20),
Container(
width: double.infinity,
height: 400,
// constraints: const BoxConstraints(
// minHeight: 180,
// ),
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.white,
@ -454,15 +535,14 @@ class _policiesState extends State<policies>
const SizedBox(height: 14),
/// SCROLLABLE GRID
Expanded(
child: activePolicies.isEmpty
activePolicies.isEmpty
? stausVal == 0 ? _EmptyBox('You dont have any expired policies at the moment.') : _EmptyBox('No active policies found')
: _PolicyGrid(
policies: activePolicies,
isEnrollment: false,
onBulkDownload: getEcardBulkDownload,
),
),
const SizedBox(height: 8),
Align(
@ -513,123 +593,158 @@ class _PolicyGrid extends StatelessWidget {
) {
final width = MediaQuery.of(context).size.width;
if (width < 600) {
return ResponsiveGridConfig(1, isEnrollment ? 1.25 : 1.15);
} else if (width < 900) {
return ResponsiveGridConfig(2, isEnrollment ? 1.6 : 1.45);
} else if (width < 1400) {
return ResponsiveGridConfig(3, isEnrollment ? 3.1 : 2.5);
// if (width < 600) {
// return ResponsiveGridConfig(1, isEnrollment ? 1.25 : 1.15);
// } else if (width < 900) {
// return ResponsiveGridConfig(2, isEnrollment ? 1.6 : 1.45);
// } else if (width < 1400) {
// return ResponsiveGridConfig(3, isEnrollment ? 3.1 : 2.5);
// } else {
// return ResponsiveGridConfig(4, isEnrollment ? 3.1 : 2.4);
// }
if (width >= 1400) {
return const ResponsiveGridConfig(4, 2.6); // Big screen
} else if (width >= 1000) {
return const ResponsiveGridConfig(3, 2.3); // Small desktop
} else if (width >= 600) {
return const ResponsiveGridConfig(2, 2.0); // Tablet
} else {
return ResponsiveGridConfig(4, isEnrollment ? 3.1 : 2.4);
return const ResponsiveGridConfig(1, 1.8); // Mobile
}
}
@override
@override
Widget build(BuildContext context) {
final tokenService = TokenStorageService();
final config = _getGridConfig(context, isEnrollment);
return GridView.builder(
physics: const BouncingScrollPhysics(),
padding: EdgeInsets.zero,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: config.crossAxisCount,
childAspectRatio: config.childAspectRatio,
crossAxisSpacing: 16,
mainAxisSpacing: 16,
final int rowCount = (policies.length / config.crossAxisCount).ceil();
double cardHeight;
if (isEnrollment) {
cardHeight = 140;
} else {
cardHeight = 170;
}
final double totalHeight =
rowCount * cardHeight + ((rowCount - 1) * 16);
return SizedBox(
height: totalHeight,
child: GridView.builder(
physics: const BouncingScrollPhysics(),
shrinkWrap: true,
padding: EdgeInsets.zero,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: config.crossAxisCount,
childAspectRatio: config.childAspectRatio,
crossAxisSpacing: 16,
mainAxisSpacing: 16,
mainAxisExtent: isEnrollment ? 120 : 150, // fixed card height
),
itemCount: policies.length,
itemBuilder: (context, index) {
final data = policies[index];
return isEnrollment
? _EnrollmentPolicyCardNew(
data: data,
onTap: () async {
final token = await tokenService.getCurrentToken();
final clientId =
await tokenService.readValue('enrollmentClient_id');
final branchId = await tokenService
.readValue('enrollmentEmpClientBranchId');
if (token == null || clientId == null || branchId == null) {
return;
}
Navigator.push(
context,
MaterialPageRoute(
settings: const RouteSettings(name: 'hrPolicyDetails'),
builder: (_) => hrPolicyDetails(
ClientId: clientId,
policyTypeId:
data['policy_type_id'].toString(),
ClientPoliyId:
data['client_policy_id'].toString(),
clientBranchId: branchId,
Token: token,
TokenType: "pre",
cardType: data['type'].toString(),
cardPolicyNo: data['policy_no'].toString(),
cardInsurer_name:
data['insurer_short_name'].toString(),
cardPolicy_name:
data['policy_name'].toString(),
cardPolicy_ExpDate:
data['policy_expiry_date'].toString(),
total_premium: '',
is_ecard_bulk_download_for_employee: 0,
),
),
);
},
)
: _ActivePolicyCardNew(
data: data,
onBulkDownload: onBulkDownload,
onTap: () async {
final token = await tokenService.getCurrentToken();
final clientId = await tokenService.readValue('empClientId');
final branchId = await tokenService.readValue('empClientBranchId');
print("token: $token");
print("clientId: $clientId");
print("branchId: $branchId");
if (token == null || clientId == null || branchId == null) {
print("Missing required values");
return;
}
Navigator.push(
context,
MaterialPageRoute(
settings: const RouteSettings(name: 'hrPolicyDetails'),
builder: (_) => hrPolicyDetails(
ClientId: clientId,
policyTypeId:
data['policy_type_id'].toString(),
ClientPoliyId:
data['client_policy_id'].toString(),
clientBranchId: branchId,
Token: token,
TokenType: 'post',
cardType: data['type'].toString(),
cardPolicyNo: data['policy_no'].toString(),
cardInsurer_name:
data['insurer_short_name'].toString(),
cardPolicy_name:
data['policy_name'].toString(),
cardPolicy_ExpDate:
data['policy_expiry_date'].toString(),
total_premium:
data['total_premium'].toString(),
is_ecard_bulk_download_for_employee:
data['is_ecard_bulk_download_for_employee'],
),
),
);
},
);
},
),
itemCount: policies.length,
itemBuilder: (context, index) {
final data = policies[index];
return isEnrollment
? _EnrollmentPolicyCardNew(
data: data,
onTap: () async {
final token = await tokenService.getCurrentToken();
final clientId =
await tokenService.readValue('enrollmentClient_id');
final branchId = await tokenService
.readValue('enrollmentEmpClientBranchId');
if (token == null || clientId == null || branchId == null) {
return;
}
Navigator.push(
context,
MaterialPageRoute(
settings: const RouteSettings(name: 'hrPolicyDetails'),
builder: (_) => hrPolicyDetails(
ClientId: clientId,
policyTypeId:
data['policy_type_id'].toString(),
ClientPoliyId:
data['client_policy_id'].toString(),
clientBranchId: branchId,
Token: token,
TokenType: "pre",
cardType: data['type'].toString(),
cardPolicyNo: data['policy_no'].toString(),
cardInsurer_name:
data['insurer_short_name'].toString(),
cardPolicy_name:
data['policy_name'].toString(),
cardPolicy_ExpDate:
data['policy_expiry_date'].toString(),
total_premium: '',
is_ecard_bulk_download_for_employee: 0,
),
),
);
},
)
: _ActivePolicyCardNew(
data: data,
onBulkDownload: onBulkDownload,
onTap: () async {
final token = await tokenService.getCurrentToken();
final clientId =
await tokenService.readValue('empClientId');
final branchId =
await tokenService.readValue('empClientBranchId');
if (token == null || clientId == null || branchId == null) {
return;
}
Navigator.push(
context,
MaterialPageRoute(
settings: const RouteSettings(name: 'hrPolicyDetails'),
builder: (_) => hrPolicyDetails(
ClientId: clientId,
policyTypeId:
data['policy_type_id'].toString(),
ClientPoliyId:
data['client_policy_id'].toString(),
clientBranchId: branchId,
Token: token,
TokenType: 'post',
cardType: data['type'].toString(),
cardPolicyNo: data['policy_no'].toString(),
cardInsurer_name:
data['insurer_short_name'].toString(),
cardPolicy_name:
data['policy_name'].toString(),
cardPolicy_ExpDate:
data['policy_expiry_date'].toString(),
total_premium:
data['total_premium'].toString(),
is_ecard_bulk_download_for_employee:
data['is_ecard_bulk_download_for_employee'],
),
),
);
},
);
},
);
}
}

View File

@ -51,8 +51,7 @@ class postFileUpload extends StatefulWidget {
required this.cardInsurer_name,
required this.cardPolicy_name,
required this.cardPolicy_ExpDate,
required this.total_premium
})
required this.total_premium})
: super(key: key);
@override
@ -61,6 +60,23 @@ class postFileUpload extends StatefulWidget {
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 = '';
Uint8List? fileBytes;
Uint8List? fileBytes2;
late String _token;
@ -101,9 +117,8 @@ class _postFileUploadState extends State<postFileUpload> {
bool showSampleButton = false;
String? currentApiValue; // To store the 'value' for the 2nd param
int _currentPage = 1;
int _rowsPerPage = 5;
int _rowsPerPage = 6;
List<dynamic> get _paginatedData {
final startIndex = (_currentPage - 1) * _rowsPerPage;
@ -125,9 +140,12 @@ class _postFileUploadState extends State<postFileUpload> {
void initState() {
super.initState();
apiService = ApiService(context);
_loadToken();
getFileUploadMasterDetails();
getFileListDetails();
restoreUploadData().then((_) {
_loadToken();
getFileUploadMasterDetails();
getFileListDetails();
});
}
@override
@ -137,22 +155,85 @@ class _postFileUploadState extends State<postFileUpload> {
}
Future<void> _loadToken() async {
// final token = prefs.getString('hrtoken');
final token = widget.Token;
final token = localToken.isNotEmpty
? localToken
: await tokenService.readValue('upload_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> 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'];
@ -184,7 +265,7 @@ class _postFileUploadState extends State<postFileUpload> {
Future<void> getFileUploadMasterDetails() async {
print('9');
try {
final response = await apiService.getFileUploadMastersToApi(widget.Token);
final response = await apiService.getFileUploadMastersToApi(localToken);
if (response['status'] == true) {
print('getFileUploadMasterList1');
@ -220,8 +301,8 @@ class _postFileUploadState extends State<postFileUpload> {
print('9');
try {
final response = await apiService.getFileListToApi(
empPrimaryId, widget.cardPolicyNo, empClientId,widget.Token,widget.TokenType);
final response = await apiService.getFileListToApi(empPrimaryId,
localCardPolicyNo, empClientId, localToken, localTokenType);
if (response['status'] == 'success') {
print('getThrFileList');
@ -248,18 +329,20 @@ class _postFileUploadState extends State<postFileUpload> {
}
Future<void> getHrFileDownload(id, file_name) async {
// final http.Response response = await apiService.getHrFileDownloadToApi(id, widget.Token);
// final http.Response response = await apiService.getHrFileDownloadToApi(id, localToken);
print("**********-------*****");
final encryptClientId = widget.ClientId;
final encryptClientId = localClientId;
print(encryptClientId);
final apiurl = Environment.apiUrlPost;
final String url = '$apiurl/hrFileDownload?id=$id&cliend_id=$encryptClientId';
final token = widget.Token;
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',
'APP-SIGNATURE':
'nhance-2025-signature-T8f6gV9k5fK2wf5V5c4vId3b0J8z6cAm2x8Y',
'Authorization': 'Bearer $token',
'Content-Type': 'application/json',
// 'app-signature': 'ts-traveltool-2025-signature-123456',
@ -297,16 +380,17 @@ class _postFileUploadState extends State<postFileUpload> {
Future<void> downloadPostSampleFile(String apiParam) async {
print("fun Sam f - in");
final post_file_name = apiParam+'_sample_file.xlsx';
print("fun Sam f - name $post_file_name" );
final post_file_name = apiParam + '_sample_file.xlsx';
print("fun Sam f - name $post_file_name");
final apiurl = Environment.apiUrlPost;
final String url = '$apiurl/downloadSampleExcel/$apiParam';
final token = widget.Token;
final token = localToken;
final response = await http.get(
Uri.parse(url),
headers: {
'APP-SIGNATURE': 'nhance-2025-signature-T8f6gV9k5fK2wf5V5c4vId3b0J8z6cAm2x8Y',
'APP-SIGNATURE':
'nhance-2025-signature-T8f6gV9k5fK2wf5V5c4vId3b0J8z6cAm2x8Y',
'Authorization': 'Bearer $token',
'Content-Type': 'application/json',
// 'app-signature': 'ts-traveltool-2025-signature-123456',
@ -315,7 +399,7 @@ class _postFileUploadState extends State<postFileUpload> {
if (response.statusCode == 200) {
try {
print("fun sam f - ${response.statusCode}" );
print("fun sam f - ${response.statusCode}");
// Create a blob from the response body bytes
final blob = html.Blob([response.bodyBytes]);
@ -333,7 +417,7 @@ class _postFileUploadState extends State<postFileUpload> {
ToastHelper.showSuccessToast(context, 'File Downloaded Successfully');
} catch (e) {
print("fun sam f - fail" );
print("fun sam f - fail");
throw Exception('Error parsing response: $e');
}
} else {
@ -475,7 +559,8 @@ class _postFileUploadState extends State<postFileUpload> {
print('else');
// Attach the file to the request
// Set authorization token in headers
request.headers['APP-SIGNATURE'] = 'nhance-2025-signature-T8f6gV9k5fK2wf5V5c4vId3b0J8z6cAm2x8Y';
request.headers['APP-SIGNATURE'] =
'nhance-2025-signature-T8f6gV9k5fK2wf5V5c4vId3b0J8z6cAm2x8Y';
request.headers['Authorization'] = 'Bearer $_token';
// request.files.add(http.MultipartFile.fromBytes('file', fileBytes,
// filename: fileName));
@ -492,13 +577,13 @@ class _postFileUploadState extends State<postFileUpload> {
));
print('clintID: $clintID');
request.fields['client_id'] = widget.ClientId;
request.fields['policy_no'] = widget.cardPolicyNo;
request.fields['client_branch_id'] = widget.clientBranchId;
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'] = widget.ClientPoliyId;
request.fields['policy_id'] = localClientBranchId;
// "client_id": 1,
// "client_branch_id": 2,
// "policy_no": "POL123456",
@ -520,15 +605,15 @@ class _postFileUploadState extends State<postFileUpload> {
isLoading = false;
});
ToastHelper.showSuccessToast(context, data['message']);
getFileListDetails();
setState(() {
selectedValue = null;
selectedKey = null;
resetErrorCount();
getFileListDetails();
});
} else {
getFileListDetails();
setState(() {
getFileListDetails();
isLoading = false;
selectedValue = null;
selectedKey = null;
@ -587,217 +672,267 @@ class _postFileUploadState extends State<postFileUpload> {
}
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: () => {Navigator.pop(context)},
icon: const Icon(
Icons.arrow_back_ios,
size: 18,
color: Colors.black,
),
padding: EdgeInsets.zero,
constraints: const BoxConstraints(),
),
const SizedBox(width: 6),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
// mainAxisAlignment: MainAxisAlignment.start,
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: [
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),
),
],
),
),
// 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),
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;
});
},
),
),
IconButton(
tooltip: 'Previous Page',
onPressed: () async {
if (Navigator.canPop(context)) {
Navigator.pop(context);
return;
}
const SizedBox(width: 16),
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';
/// 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,
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),
),
],
),
),
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',
);
}
// 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 ?? '');
},
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,
),
],
),
)
);
},
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),
Row(
children: [
Expanded(
child: Column(
children: [
_buildFileUploadedGrid(),
const SizedBox(height: 16),
_buildPagination(context),
],
),
)
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({
@ -852,8 +987,6 @@ class _postFileUploadState extends State<postFileUpload> {
);
}
Widget buildStyledDropdown({
required String label,
required String? value,
@ -906,7 +1039,6 @@ class _postFileUploadState extends State<postFileUpload> {
);
}
Widget _buildFileUploadedGrid() {
if (filteredData.isEmpty) {
return const SizedBox(
@ -916,20 +1048,23 @@ class _postFileUploadState extends State<postFileUpload> {
}
return GridView.builder(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
shrinkWrap: true, // IMPORTANT
physics: const NeverScrollableScrollPhysics(), // Disable inner scroll
padding: const EdgeInsets.all(16),
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);
},
);
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) {
@ -1014,9 +1149,7 @@ class _postFileUploadState extends State<postFileUpload> {
children: [
/// 🔴 Error + Status
Row(
children: [
],
children: [],
),
const SizedBox(height: 8),
@ -1024,58 +1157,60 @@ class _postFileUploadState extends State<postFileUpload> {
Row(
children: [
if (item['file_error_status'] == '1')
InkWell(
onTap: () async {
print(item);
// return;
final String? token = await tokenService.getCurrentToken();
final String? empClientId = await tokenService.readValue('empClientId');
final String? empBranchId = await tokenService.readValue('empClientBranchId');
InkWell(
onTap: () async {
print(item);
// return;
final String? token =
await tokenService.getCurrentToken();
final String? empClientId =
await tokenService.readValue('empClientId');
final String? empBranchId =
await tokenService.readValue('empClientBranchId');
print(item);
print(empClientId);
print(widget.policyTypeId);
print(empBranchId);
print(token);
print('post');
print(widget.cardType);
print(widget.cardPolicyNo);
print(widget.cardInsurer_name);
print(widget.cardPolicy_name);
print(widget.cardPolicy_ExpDate);
print(item['id']);
print(item);
print(empClientId);
print(localPolicyTypeId);
print(empBranchId);
print(token);
print('post');
print(localCardType);
print(localCardPolicyNo);
print(localCardInsurerName);
print(localCardPolicyName);
print(localCardPolicyExpDate);
print(item['id']);
// SAFETY CHECK
if (token == null ||
empClientId == null ||
empBranchId == null) {
debugPrint('❌ Missing required data for navigation ${token}');
return;
}
// 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,
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),
@ -1100,8 +1235,8 @@ class _postFileUploadState extends State<postFileUpload> {
),
],
),
/// Download
/// Download
],
),
],
@ -1109,7 +1244,6 @@ class _postFileUploadState extends State<postFileUpload> {
);
}
Widget _buildStatusChip(String status) {
final s = status.toLowerCase();
@ -1142,7 +1276,6 @@ class _postFileUploadState extends State<postFileUpload> {
);
}
static final _dataBold = GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w400,
@ -1161,14 +1294,14 @@ class _postFileUploadState extends State<postFileUpload> {
);
Widget _buildPagination(BuildContext context) {
final totalItems = filteredData.length;
final int startEntry = totalItems == 0 ? 0 : ((_currentPage - 1) * _rowsPerPage) + 1;
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 = 5;
const visiblePageCount = 6;
List<int> getVisiblePages() {
if (totalPages <= visiblePageCount) {
@ -1177,7 +1310,8 @@ class _postFileUploadState extends State<postFileUpload> {
if (_currentPage <= 3) {
return [1, 2, 3, 4, 5];
} if (_currentPage >= totalPages - 2) {
}
if (_currentPage >= totalPages - 2) {
return [
totalPages - 4,
totalPages - 3,
@ -1186,14 +1320,13 @@ class _postFileUploadState extends State<postFileUpload> {
totalPages
];
}
return [
_currentPage - 2,
_currentPage - 1,
_currentPage,
_currentPage + 1,
_currentPage + 2,
];
return [
_currentPage - 2,
_currentPage - 1,
_currentPage,
_currentPage + 1,
_currentPage + 2,
];
}
List<int> visiblePages = getVisiblePages();
@ -1208,7 +1341,7 @@ class _postFileUploadState extends State<postFileUpload> {
// Dropdown for rows per page
DropdownButton<int>(
value: _rowsPerPage,
items: [5, 10, 15, 20, 50].map((int value) {
items: [6, 10, 15, 20, 50].map((int value) {
return DropdownMenuItem<int>(
value: value,
child: Text(' $value ',

File diff suppressed because it is too large Load Diff

View File

@ -12,6 +12,8 @@ class ApiService {
String? _token;
String? _hrtoken;
bool _isSessionOutToastShown = false; // Flag to track toast message
bool isTpaDashboardEnabled = false;
bool isTpaSelected = false; // track which dashboard is active
ApiService(this.context) {
_initializeToken();
@ -676,9 +678,32 @@ class ApiService {
}
}
Future<Map<String, dynamic>> getClaimPoliciesToApi(String token) async {
Future<Map<String, dynamic>> postHrTpaDashboard(params, token) async {
final url = Uri.parse('${Environment.apiUrlPost}getHrTpaDashboard');
final headers = {
'Authorization': 'Bearer ${token ?? ''}',
'APP-SIGNATURE': 'nhance-2025-signature-T8f6gV9k5fK2wf5V5c4vId3b0J8z6cAm2x8Y',
};
final response = await http.post(
url,
headers: headers,
body: jsonEncode(params),
);
if (response.statusCode == 200) {
return jsonDecode(response.body);
} else {
throw Exception(
'Failed to load HR TPA dashboard: ${response.statusCode}');
}
}
Future<Map<String, dynamic>> getClaimPoliciesToApi(String token,empClientId) async {
print("getgetClaimPoliciesToApii1");
final url = Uri.parse('${Environment.apiUrlPost}claimsSearch');
final url = Uri.parse('${Environment.apiUrlPost}claimsSearch?client_id=$empClientId');
final headers = {
'Authorization': 'Bearer $token' ?? '',
@ -993,6 +1018,10 @@ class ApiService {
}
Future<Map<String, dynamic>> _handleResponse(http.Response response) async {
// 'throttle' => 429,
// 'soft' => 429,
// 'medium' => 403,
// 'hard' => 451,
if (response.statusCode == 200) {
return jsonDecode(response.body);
} else if (response.statusCode == 401) {
@ -1001,6 +1030,17 @@ class ApiService {
await _clearLocalStorageAndRedirect();
}
return {};
} else if (response.statusCode == 403) {
if (!_isSessionOutToastShown) {
_isSessionOutToastShown = true;
await _clearLocalStorageAndRedirect();
}
return {};
}else if (response.statusCode == 451) {
final body = jsonDecode(response.body);
final message = body['message'];
ToastHelper.showWarningToast(context, message);
return {};
} else if (response.statusCode == 429) {
final body = jsonDecode(response.body);
final message = body['message'];

View File

@ -11,6 +11,7 @@ class MultiFileUploadWidget extends StatefulWidget {
State<MultiFileUploadWidget> createState() => _MultiFileUploadWidgetState();
static bool hasFiles = false;
static bool showValidation = false;
}
class _MultiFileUploadWidgetState extends State<MultiFileUploadWidget> {
@ -44,6 +45,7 @@ class _MultiFileUploadWidgetState extends State<MultiFileUploadWidget> {
setState(() {
errorMessage = null;
MultiFileUploadWidget.hasFiles = fileService.files.isNotEmpty;
MultiFileUploadWidget.showValidation = false;
});
}
}
@ -52,6 +54,9 @@ class _MultiFileUploadWidgetState extends State<MultiFileUploadWidget> {
fileService.removeFileAt(index);
setState(() {
MultiFileUploadWidget.hasFiles = fileService.files.isNotEmpty;
if (fileService.files.isEmpty) {
MultiFileUploadWidget.showValidation = true;
}
});
}
@ -173,7 +178,9 @@ class _MultiFileUploadWidgetState extends State<MultiFileUploadWidget> {
// ),
],
if (fileService.files.isEmpty && errorMessage == null) ...[
if (MultiFileUploadWidget.showValidation &&
fileService.files.isEmpty &&
errorMessage == null) ...[
const SizedBox(height: 4),
const Text(
"Required",

View File

@ -241,6 +241,9 @@ class TokenStorageService {
await _secureStorage.write(key: key, value: value);
}
Future<void> removeValue(String key) async {
await _secureStorage.delete(key: key);
}
Future<void> clearBranchSession() async {
final keysToRemove = [

View File

@ -33,6 +33,26 @@
<link rel="manifest" href="manifest.json">
<style>
/* Hide card three-dot menu */
[data-testid="dashcard-action-panel"] {
display: none !important;
}
/* Fallback selector */
.DashCard-actions {
display: none !important;
}
/* Hide powered by footer */
.MetabasePoweredBy {
display: none !important;
}
/* Fallback */
a[href*="metabase.com"] {
display: none !important;
}
.content {
width: 10%;
height: 10vh;