enrollment-app/lib/presentation/policies.dart
2026-03-31 12:47:07 +05:30

1331 lines
44 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import 'dart:convert';
import 'package:flutter/cupertino.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:jwt_decode/jwt_decode.dart';
import 'package:nhancepolicy/responsive.dart';
import 'package:nhancepolicy/service/api_service.dart';
import 'package:nhancepolicy/service/hrDashboardTabs/activePolicies.dart';
import 'package:nhancepolicy/service/hrDashboardTabs/cd.dart';
import 'package:nhancepolicy/presentation/claims.dart';
import 'package:nhancepolicy/presentation/postFileUpload.dart';
import 'package:nhancepolicy/service/hrDashboardTabs/preEnrollment.dart';
import 'package:nhancepolicy/service/token_storage_service.dart';
import 'package:nhancepolicy/service/_ResponsiveGridConfig.dart';
import 'package:http/http.dart' as http;
import 'package:universal_html/html.dart' as html;
import 'package:intl/intl.dart';
import '../config/environment.dart';
import '../customAppBar/base_layout.dart';
import '../customAppBar/toastHelper.dart';
import '../service/secure_pop_scope.dart';
import 'hrPolicyDetails.dart';
import 'package:nhancepolicy/logger.dart';
class policies extends StatefulWidget {
const policies({Key? key}) : super(key: key);
@override
State<policies> createState() => _policiesState();
}
class _policiesState extends State<policies>
with SingleTickerProviderStateMixin {
late ApiService apiService;
bool isLoading = false;
int isHrcode = 0;
String? _postPreToken = '';
dynamic enrollmentClient_id;
dynamic policy_name;
dynamic getPreCardArrays = [];
dynamic getPostCardArrays = [];
dynamic empClientBranchId;
dynamic empHrId;
int stausVal = 1;
int selectedIndex = 1;
dynamic enrollmentEmpClientBranchId;
dynamic enrollmentHrId;
dynamic empClientId;
String empCodeFromHrPolcy = '';
List<Map<String, dynamic>> openForEnrollmentList = [];
List<Map<String, dynamic>> activePoliciesList = [];
final tokenService = TokenStorageService();
List<int> postModules = [];
List<int> enrollmentModules = [];
@override
void initState() {
super.initState();
apiService = ApiService(context); // Initialize ApiService here
checkToken();
// empCodeFromHrPolcy = widget.empCodeFromHrPolicy;
// _tabController = TabController(length: 4, vsync: this);
// _tabController.addListener(() {
// setState(() {
// selectedIndex = _tabController.index;
// });
// });
// Future.delayed(Duration(seconds: 3), () {
// setState(() {
// isLoading = false;
// });
// });
}
@override
void dispose() {
super.dispose();
}
checkToken() async {
// enrollToken = prefs.getString('pre_enrollment_data');
// _postToken = prefs.getString('post_enrollment_data');
// Get the current token
final token = await tokenService.getCurrentToken();
logDebug('token - $token');
logDebug('token check in');
if ((token != null && token!.isNotEmpty)) {
logDebug('token check done');
_loadToken();
} else {
logDebug('token check reject');
ToastHelper.showErrorToast(context, 'Session Out');
Navigator.pushReplacementNamed(context, 'hrLogin');
}
}
// 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))
// : [];
//
// logDebug('enrollmentModules $enrollmentModules');
// logDebug('postModules $postModules');
//
// _postPreToken = await tokenService.getCurrentToken();
// logDebug(_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 {
setState(() {
isLoading = true; // 🔥 START LOADER HERE
});
try {
final enrollmentRaw =
await tokenService.readValue('enrollmentAllowed_modules');
final postRaw = await tokenService.readValue('empAllowed_modules');
enrollmentModules = enrollmentRaw != null && enrollmentRaw.isNotEmpty
? List<int>.from(jsonDecode(enrollmentRaw))
: [];
postModules = postRaw != null && postRaw.isNotEmpty
? List<int>.from(jsonDecode(postRaw))
: [];
_postPreToken = await tokenService.getCurrentToken();
List<Future> apiCalls = [];
/// 👇 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) {
logDebug("Error in _loadToken: $e");
} finally {
if (mounted) {
setState(() {
isLoading = false; // 🔥 STOP LOADER ONLY ONCE
});
}
}
}
Future<void> getPreCashDepositDetails(enrollmentEmpClientBranchId,
enrollmentClient_id, enrollmentHrId, _postPreToken) async {
logDebug('IN');
logDebug("clintBranchId -$enrollmentEmpClientBranchId");
logDebug("clintID -$enrollmentClient_id");
logDebug("hr_id -$enrollmentHrId");
logDebug("token -$_postPreToken");
// setState(() {
// _isLoading = true;
// });
try {
if (enrollmentEmpClientBranchId == null || enrollmentClient_id == null) {
return;
}
final response = await apiService.getCashDepositDetailsToApi(
enrollmentClient_id!,
enrollmentEmpClientBranchId!,
enrollmentHrId,
_postPreToken);
// final response = await apiService.getCashDepositDetailsToApi(
// clintID!, clintBranchId!, hr_id, token);
logDebug('IN1');
if (response['status'] == 'success') {
setState(() {
logDebug('response');
logDebug(response['data']);
getPreCardArrays = List<Map<String, dynamic>>.from(response['data']);
logDebug('getPreCardArrays');
logDebug(getPreCardArrays);
openForEnrollmentList = getPreCardArrays;
});
logDebug('IN2');
} else {
logDebug('API request failed with status');
}
} catch (e) {
logDebug('Exception occurred: $e');
}
}
Future<void> getPostCashDepositDetails(
empClientBranchId, empClientId, empHrId, _postPreToken) async {
logDebug('IN');
logDebug("clintBranchId -$empClientBranchId");
logDebug("clintID -$empClientId");
logDebug("hr_id -$empHrId");
logDebug("token -$_postPreToken");
// setState(() {
// _isLoading = true;
// });
try {
if (empClientBranchId == null || empClientId == null) {
return;
}
final response = await apiService.getActiveCashDepositDetailsToApi(
empClientId!, empClientBranchId!, empHrId, _postPreToken, stausVal);
// final response = await apiService.getCashDepositDetailsToApi(
// clintID!, clintBranchId!, hr_id, token);
logDebug('IN1');
if (response['status'] == 'success') {
setState(() {
logDebug('response');
logDebug(response['data']);
getPostCardArrays = List<Map<String, dynamic>>.from(response['data']);
logDebug('getPostCardArrays');
logDebug(getPostCardArrays);
activePoliciesList = getPostCardArrays;
});
logDebug('IN2');
} else {
logDebug('API request failed with status');
setState(() {
activePoliciesList = [];
});
logDebug('API request failed with status');
}
} catch (e) {
logDebug('Exception occurred: $e');
}
}
Future<bool> _showLogoutDialog() async {
return await showDialog<bool>(
context: context,
barrierDismissible: false,
builder: (context) => AlertDialog(
title: Text("Confirm Logout"),
content: Text("Do you want to logout?"),
actions: [
TextButton(
onPressed: () => Navigator.of(context).pop(false),
child: Text("Cancel"),
),
TextButton(
onPressed: () => Navigator.of(context).pop(true),
child: Text("Logout"),
),
],
),
) ??
false;
}
Future<void> getEcardBulkDownload(clientPolicyId) async {
try {
logDebug('10');
empHrId = await tokenService.readValue('empHrId');
final response = await apiService.getEcardBulkDownloadApi(
clientPolicyId, empHrId, '', _postPreToken!);
if (response['status'] == true) {
logDebug('Request success');
_showBulkDownloadSuccessPopup(response['message']);
} else {
ToastHelper.showErrorToast(context, response['message']);
logDebug('Request failed with status: ${response['code']}');
}
} catch (e) {
logDebug('Exception occurred: $e');
}
}
void _showBulkDownloadSuccessPopup(String message) {
showDialog(
context: context,
barrierDismissible: false,
builder: (context) {
return AlertDialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16),
),
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
const Icon(
Icons.check_circle,
color: Color(0xFF009195),
size: 60,
),
const SizedBox(height: 16),
Text(
message,
textAlign: TextAlign.center,
style: GoogleFonts.poppins(
fontSize: 14,
fontWeight: FontWeight.w500,
),
),
const SizedBox(height: 24),
SizedBox(
width: double.infinity,
child: ElevatedButton(
onPressed: () => Navigator.pop(context),
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFF009195),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
),
child: Text(
'OK',
style: GoogleFonts.poppins(color: Colors.white),
),
),
),
],
),
);
},
);
}
@override
Widget build(BuildContext context) {
return BaseLayout(
child: SecurePopScope(
child: buildPoliciesBody(
openEnrollment: openForEnrollmentList,
activePolicies: activePoliciesList,
),
),
);
}
Widget buildPoliciesBody({
required List<Map<String, dynamic>> openEnrollment,
required List<Map<String, dynamic>> activePolicies,
}) {
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),
child: Container(
color: const Color(0xFFF5F7F7), // 👈 same light grey as dashboard
// padding: const EdgeInsets.all(20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
// IconButton(
// onPressed: () {
// _showLogoutDialog();
// },
// icon: const Icon(
// Icons.arrow_back_ios,
// size: 20,
// color: Colors.black,
// ),
// padding: EdgeInsets.zero,
// constraints: const BoxConstraints(),
// ),
const SizedBox(width: 5),
Text(
'Policies',
style: GoogleFonts.poppins(
fontSize: 22,
fontWeight: FontWeight.w500,
color: Colors.black,
),
),
],
),
if (enrollmentModules.contains(1)) ...[
SizedBox(height: 15),
Container(
width: double.infinity,
// constraints: const BoxConstraints(
// minHeight: 180,
// ),
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(6),
),
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,
),
],
),
)),
],
if (postModules.contains(2)) ...[
SizedBox(height: 20),
Container(
width: double.infinity,
// 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: [
/// HEADER
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'Active Policies',
style: GoogleFonts.poppins(
fontSize: 14, fontWeight: FontWeight.w600),
),
_ActiveExpiredToggle(
selectedIndex: selectedIndex,
onChange: (value) async {
setState(() {
selectedIndex = value;
stausVal = value == 1 ? 1 : 0;
});
await getPostCashDepositDetails(
empClientBranchId,
empClientId,
empHrId,
_postPreToken,
);
},
),
],
),
const SizedBox(height: 14),
/// ✅ SCROLLABLE GRID
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(
alignment: Alignment.bottomRight,
child: Text(
'* Premium may vary subject to claims',
style: GoogleFonts.poppins(
fontSize: 10, color: Colors.red),
),
),
],
),
),
]
],
),
)),
);
}
}
class ResponsiveGridConfig {
final int crossAxisCount;
final double childAspectRatio;
const ResponsiveGridConfig(
this.crossAxisCount,
this.childAspectRatio,
);
}
class _PolicyGrid extends StatelessWidget {
final List<Map<String, dynamic>> policies;
final bool isEnrollment;
final Function(String clientPolicyId)? onBulkDownload;
const _PolicyGrid({
super.key,
required this.policies,
required this.isEnrollment,
this.onBulkDownload,
});
bool _isNonEbPolicy(Map<String, dynamic> data) {
final allocg = (data['allocg'] ?? '').toString().trim().toLowerCase();
return allocg == 'non-eb';
}
ResponsiveGridConfig _getGridConfig(
BuildContext context,
bool isEnrollment,
) {
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);
// } 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 const ResponsiveGridConfig(1, 1.8); // Mobile
}
}
@override
Widget build(BuildContext context) {
final tokenService = TokenStorageService();
final config = _getGridConfig(context, isEnrollment);
/// ✅ Enrollment → No change
if (isEnrollment) {
return _buildGrid(
context,
policies,
config,
tokenService,
);
}
/// ✅ Active → Split EB / Non-EB
final ebPolicies = policies.where((p) => p['allocg'] == 'EB').toList();
final nonEbPolicies =
policies.where((p) => p['allocg'] == 'Non-EB').toList();
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
/// ✅ EB section
if (ebPolicies.isNotEmpty) ...[
Text(
'EB Policies',
style: GoogleFonts.poppins(
fontSize: 13,
fontWeight: FontWeight.w600,
color: const Color(0xFF009195),
),
),
const SizedBox(height: 10),
_buildGrid(
context,
ebPolicies,
config,
tokenService,
),
],
/// ✅ Non-EB section
if (nonEbPolicies.isNotEmpty) ...[
Text(
'Non-EB Policies',
style: GoogleFonts.poppins(
fontSize: 13,
fontWeight: FontWeight.w600,
color: const Color(0xFFD06A00),
),
),
const SizedBox(height: 10),
_buildGrid(
context,
nonEbPolicies,
config,
tokenService,
isNonEb: true,
),
],
],
);
}
Widget _buildGrid(
BuildContext context,
List<Map<String, dynamic>> list,
ResponsiveGridConfig config,
TokenStorageService tokenService, {
bool isNonEb = false,
}) {
final int rowCount = (list.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: list.length,
itemBuilder: (context, index) {
final data = list[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,
isNonEb: isNonEb,
onBulkDownload: onBulkDownload,
onTap: () async {
final token = await tokenService.getCurrentToken();
final clientId =
await tokenService.readValue('empClientId');
final branchId =
await tokenService.readValue('empClientBranchId');
logDebug("token: $token");
logDebug("clientId: $clientId");
logDebug("branchId: $branchId");
if (token == null || clientId == null || branchId == null) {
logDebug("Missing required values");
return;
}
if (_isNonEbPolicy(data)) {
Navigator.push(
context,
MaterialPageRoute(
settings: const RouteSettings(name: 'postFileUpload'),
builder: (_) => postFileUpload(
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(),
allocgType: 'NONEB',
),
),
);
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'],
),
),
);
},
);
},
),
);
}
}
// class _PolicyGrid extends StatelessWidget {
// final List<Map<String, dynamic>> policies;
// final bool isEnrollment;
// final Function(String clientPolicyId)? onBulkDownload;
//
// const _PolicyGrid({
// super.key,
// required this.policies,
// required this.isEnrollment,
// this.onBulkDownload,
// });
//
// @override
// Widget build(BuildContext context) {
// final tokenService = TokenStorageService();
//
// return GridView.builder(
// physics: const BouncingScrollPhysics(), // ✅ scroll enabled
// gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
// crossAxisCount: 4, // desktop
// crossAxisSpacing: 16,
// mainAxisSpacing: 16,
// childAspectRatio: isEnrollment ? 2.8 : 2.4,
// ),
// itemCount: policies.length,
// itemBuilder: (context, index) {
// final data = policies[index];
//
// return isEnrollment
// ? _EnrollmentPolicyCardNew(
// data: data,
// onTap: () async {
// final String? token = await tokenService.getCurrentToken();
// final String? enrollmentClientId = await tokenService.readValue('enrollmentClient_id');
// final String? enrollmentBranchId = await tokenService.readValue('enrollmentEmpClientBranchId');
//
// // ✅ SAFETY CHECK
// if (token == null ||
// enrollmentClientId == null ||
// enrollmentBranchId == null) {
// debugPrint('❌ Missing required data for navigation ${token}');
// return;
// }
//
// Navigator.push(
// context,
// MaterialPageRoute(
// builder: (context) => hrPolicyDetails(
// ClientId: enrollmentClientId, // ✅ now String
// policyTypeId: data['policy_type_id'].toString(),
// ClientPoliyId: data['client_policy_id'].toString(),
// clientBranchId: enrollmentBranchId, // ✅ now String
// Token: token, // ✅ now String
// 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 String? token = await tokenService.getCurrentToken();
// final String? empClientId = await tokenService.readValue('empClientId');
// final String? empBranchId = await tokenService.readValue('empClientBranchId');
//
// // ✅ SAFETY CHECK
// if (token == null ||
// empClientId == null ||
// empBranchId == null) {
// debugPrint('❌ Missing required data for navigation ${token}');
// return;
// }
//
// logDebug('$token , $empClientId, $empBranchId');
// logDebug(data);
// // return;
//
// Navigator.push(
// context,
// MaterialPageRoute(
// builder: (context) => hrPolicyDetails(
// ClientId: empClientId, // <-- from map
// policyTypeId: data['policy_type_id'].toString(), // <-- from map
// ClientPoliyId: data['client_policy_id'].toString(),
// clientBranchId: empBranchId,
// 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'],
// ),
// ),
// );
// },
// );
// },
// );
// }
// }
class _EnrollmentPolicyCardNew extends StatelessWidget {
final Map<String, dynamic> data;
final VoidCallback? onTap;
const _EnrollmentPolicyCardNew({
required this.data,
this.onTap,
});
@override
Widget build(BuildContext context) {
return MouseRegion(
cursor: SystemMouseCursors.click, // 👈 pointer on hover
child: InkWell(
borderRadius: BorderRadius.circular(12),
onTap: onTap, // 👈 card click
child: Container(
decoration: BoxDecoration(
color: const Color(0xFFE9F6FB),
borderRadius: BorderRadius.circular(12),
),
padding: const EdgeInsets.all(14),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
/// Policy Number
Tooltip(
message: '${data['type']} - ${data['policy_no']}',
waitDuration: const Duration(milliseconds: 300),
child: Text(
'${data['type']} - ${data['policy_no']}',
maxLines: 1,
overflow: TextOverflow.ellipsis,
softWrap: false,
style: GoogleFonts.poppins(
fontSize: 14,
fontWeight: FontWeight.w500,
),
),
),
// const SizedBox(height: 4),
//
// /// Insurer
// Tooltip(
// message: data['insurer_name'] ?? '',
// waitDuration: const Duration(milliseconds: 300),
// child: Text(
// data['insurer_name'] ?? '',
// maxLines: 1,
// overflow: TextOverflow.ellipsis,
// softWrap: false,
// style: GoogleFonts.poppins(
// fontSize: 11,
// color: Colors.grey,
// ),
// ),
// ),
const SizedBox(height: 10),
/// Closes On
Text(
'Closes on: ${data['policy_expiry_date'] ?? ''}',
style: GoogleFonts.poppins(
fontSize: 12,
color: Colors.red,
),
),
const SizedBox(height: 10),
/// STATUS ROW
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
_StatusPillCount(
label: 'Draft',
value: data['membersCountOfDraft'] ?? 0,
color: Colors.orange,
),
_StatusPillCount(
label: 'Under Process',
value: data['membersCountOfEnrolled'] ?? 0,
color: Colors.blue,
),
_StatusPillCount(
label: 'Total',
value: data['totalMembersCount'] ?? 0,
color: Colors.green,
),
],
),
],
),
),
),
);
}
}
class _ActivePolicyCardNew extends StatelessWidget {
final Map<String, dynamic> data;
final VoidCallback? onTap;
final Function(String clientPolicyId)? onBulkDownload;
final bool isNonEb;
const _ActivePolicyCardNew({
required this.data,
this.onTap,
this.onBulkDownload,
this.isNonEb = false,
});
@override
Widget build(BuildContext context) {
return MouseRegion(
cursor: SystemMouseCursors.click, // 👈 pointer on hover
child: InkWell(
borderRadius: BorderRadius.circular(12),
onTap: onTap, // 👈 card click
child: Container(
decoration: BoxDecoration(
color: isNonEb
? const Color(0xFFFFF3E0) // 🔥 Non-EB
: const Color(0xFFE9F6FB), // EB
borderRadius: BorderRadius.circular(12),
),
padding: const EdgeInsets.all(14),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
/// PREMIUM
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: Text(
'Premium - ₹${data['total_premium'] ?? ''}*',
style: GoogleFonts.poppins(
fontSize: 12,
color: Color(0xFF009195),
fontWeight: FontWeight.w600,
),
),
),
// 🔥 ICON FLOATING ABOVE CARD
if (data['is_ecard_bulk_download'] == 1)
Positioned(
top: 10,
right: 10,
child: GestureDetector(
onTap: () {
logDebug(
'ICON CLICKED ${data['client_policy_id']}');
onBulkDownload?.call(
data['client_policy_id'].toString(),
);
},
child: Container(
padding: const EdgeInsets.all(6),
decoration: BoxDecoration(
color: const Color(0xFF009195),
borderRadius: BorderRadius.circular(6),
),
child: const Icon(
Icons.credit_card,
color: Colors.white,
size: 16,
),
),
),
),
],
),
const SizedBox(height: 4),
/// POLICY NO + ICON
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: Text(
'${data['type']} - ${data['policy_no']}',
style: GoogleFonts.poppins(
fontSize: 14,
fontWeight: FontWeight.w500,
),
overflow: TextOverflow.ellipsis,
),
),
],
),
const SizedBox(height: 4),
/// Insurer
Tooltip(
message: data['insurer_name'] ?? '',
waitDuration: const Duration(milliseconds: 300),
child: Text(
data['insurer_name'] ?? '',
maxLines: 1,
overflow: TextOverflow.ellipsis,
softWrap: false,
style: GoogleFonts.poppins(
fontSize: 11,
color: Colors.grey,
),
),
),
const SizedBox(height: 4),
/// DATE RANGE
Text(
'${data['policy_start_date'] ?? ''} - ${data['policy_expiry_date'] ?? ''}',
style: GoogleFonts.poppins(
fontSize: 12,
color: Color(0xFF8A9B0F),
),
),
const SizedBox(height: 10),
/// ACTIVE / INACTIVE
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
_StatusPillCount(
label: 'Active',
value: data['membersCountOfActive'] ?? 0,
color: Colors.green,
),
_StatusPillCount(
label: 'Inactive',
value: data['membersCountOfInactive'] ?? 0,
color: Colors.red,
),
],
),
],
),
)));
}
}
class _StatusPillCount extends StatelessWidget {
final String label;
final int value;
final Color color;
const _StatusPillCount({
required this.label,
required this.value,
required this.color,
});
@override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.symmetric(horizontal: 18, vertical: 5),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(20),
),
child: Row(
children: [
Text(
label,
style: GoogleFonts.poppins(fontSize: 13, color: color),
),
const SizedBox(width: 8),
Text(
value.toString(),
style: GoogleFonts.poppins(
fontSize: 14,
fontWeight: FontWeight.bold,
color: color,
),
),
],
),
);
}
}
class _ActiveExpiredToggle extends StatelessWidget {
final int selectedIndex;
final Function(int) onChange;
const _ActiveExpiredToggle({
required this.selectedIndex,
required this.onChange,
});
@override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.all(3),
decoration: BoxDecoration(
color: const Color(0xFFE6F4F3),
borderRadius: BorderRadius.circular(20),
),
child: Row(
children: [
_ToggleItem(
label: 'Active',
active: selectedIndex == 1,
onTap: () => onChange(1),
),
_ToggleItem(
label: 'Expired',
active: selectedIndex == 2,
onTap: () => onChange(2),
),
],
),
);
}
}
class _ToggleItem extends StatelessWidget {
final String label;
final bool active;
final VoidCallback onTap;
const _ToggleItem({
required this.label,
required this.active,
required this.onTap,
});
@override
Widget build(BuildContext context) {
return InkWell(
onTap: onTap,
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 18, vertical: 6),
decoration: BoxDecoration(
color: active ? const Color(0xFF009195) : Colors.transparent,
borderRadius: BorderRadius.circular(16),
),
child: Text(
label,
style: GoogleFonts.poppins(
fontSize: 11,
color: active ? Colors.white : Colors.black,
fontWeight: FontWeight.w500,
),
),
),
);
}
}
class _EmptyBox extends StatelessWidget {
final String text;
const _EmptyBox(this.text);
@override
Widget build(BuildContext context) {
return Container(
height: 180,
width: double.infinity,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(6),
border: Border.all(color: Colors.black12),
),
child: Center(
child: Text(text, style: GoogleFonts.poppins(color: Colors.grey)),
),
);
}
}