dashboard fix
This commit is contained in:
parent
64d0b0b7d9
commit
440da00ee5
@ -287,6 +287,17 @@ class _NhanceSideBarState extends State<NhanceSideBar> {
|
||||
// logout(context);
|
||||
// },
|
||||
// ),
|
||||
if (postModules.isNotEmpty && postModules.contains(5))
|
||||
_SideItem(
|
||||
icon: const Icon(
|
||||
Icons.space_dashboard_outlined,
|
||||
color: Colors.white,
|
||||
size: 30,
|
||||
),
|
||||
label: "Dashboard",
|
||||
isActive: activeRoute == 'claimsOverviewDashboard',
|
||||
onTap: () => _navigate('claimsOverviewDashboard'),
|
||||
),
|
||||
|
||||
...sideMenuItems.map((item) {
|
||||
final isClaims = item['route'] == 'ClaimsPolicies';
|
||||
@ -323,34 +334,24 @@ class _NhanceSideBarState extends State<NhanceSideBar> {
|
||||
);
|
||||
}),
|
||||
|
||||
if (postModules.isNotEmpty)
|
||||
_SideItem(
|
||||
icon: const Icon(
|
||||
Icons.space_dashboard_outlined,
|
||||
color: Colors.white,
|
||||
size: 30,
|
||||
),
|
||||
label: "Dashboard",
|
||||
isActive: activeRoute == 'claimsOverviewDashboard',
|
||||
onTap: () => _navigate('claimsOverviewDashboard'),
|
||||
),
|
||||
|
||||
if (postModules.isNotEmpty && postModules.contains(5))
|
||||
_SideItem(
|
||||
// icon: Icons.dashboard,
|
||||
icon: SvgPicture.string(
|
||||
SvgService.getSvg('dashboard'),
|
||||
width: 35,
|
||||
height: 35,
|
||||
colorFilter: const ColorFilter.mode(
|
||||
Colors.white,
|
||||
BlendMode.srcIn,
|
||||
),
|
||||
),
|
||||
label: "Insights",
|
||||
isActive: activeRoute == 'hrDashboard',
|
||||
onTap: () => _navigate('hrDashboard'),
|
||||
),
|
||||
|
||||
// if (postModules.isNotEmpty && postModules.contains(5))
|
||||
// _SideItem(
|
||||
// // icon: Icons.dashboard,
|
||||
// icon: SvgPicture.string(
|
||||
// SvgService.getSvg('dashboard'),
|
||||
// width: 35,
|
||||
// height: 35,
|
||||
// colorFilter: const ColorFilter.mode(
|
||||
// Colors.white,
|
||||
// BlendMode.srcIn,
|
||||
// ),
|
||||
// ),
|
||||
// label: "Insights",
|
||||
// isActive: activeRoute == 'hrDashboard',
|
||||
// onTap: () => _navigate('hrDashboard'),
|
||||
// ),
|
||||
|
||||
const Spacer(),
|
||||
|
||||
|
||||
@ -101,6 +101,7 @@ class _HrPolicyDetailsState extends State<hrPolicyDetails>
|
||||
List<Map<String, dynamic>> getCDPolicies = [];
|
||||
bool isLoading = false;
|
||||
bool _isLoading = false;
|
||||
bool _isSendingReminder = false;
|
||||
// dynamic clintID;
|
||||
late TabController _tabController;
|
||||
// List<dynamic> dataPolicy = [];
|
||||
@ -124,6 +125,9 @@ class _HrPolicyDetailsState extends State<hrPolicyDetails>
|
||||
int _currentPage = 1;
|
||||
int _rowsPerPage = 5;
|
||||
|
||||
/// Pre-enrollment status chip filter (null = show all).
|
||||
String? _preStatusFilter;
|
||||
|
||||
/// holds selected employee ids as String or int (be consistent)
|
||||
final Set<String> selectedEmployeeIds = {};
|
||||
|
||||
@ -314,7 +318,7 @@ class _HrPolicyDetailsState extends State<hrPolicyDetails>
|
||||
setState(() {
|
||||
getCDPolicies = List<Map<String, dynamic>>.from(response['data']);
|
||||
originalData = getCDPolicies;
|
||||
filteredData = List.from(originalData);
|
||||
_refreshFilteredData();
|
||||
logDebug('filteredData');
|
||||
logDebug(filteredData);
|
||||
});
|
||||
@ -387,7 +391,7 @@ class _HrPolicyDetailsState extends State<hrPolicyDetails>
|
||||
setState(() {
|
||||
getCDPolicies = List<Map<String, dynamic>>.from(data);
|
||||
originalData = getCDPolicies;
|
||||
filteredData = List.from(originalData);
|
||||
_refreshFilteredData();
|
||||
isLoading = false;
|
||||
});
|
||||
} else {
|
||||
@ -455,65 +459,100 @@ class _HrPolicyDetailsState extends State<hrPolicyDetails>
|
||||
}
|
||||
}
|
||||
|
||||
void search(String query) {
|
||||
final lowerQuery = query.toLowerCase().trim();
|
||||
bool _isEnrolledStatus(String status) =>
|
||||
status == 'under process' || status == 'enrolled';
|
||||
|
||||
if (lowerQuery.isEmpty) {
|
||||
setState(() {
|
||||
_currentPage = 1;
|
||||
filteredData = List.from(originalData);
|
||||
});
|
||||
} else {
|
||||
setState(() {
|
||||
_currentPage = 1;
|
||||
filteredData = originalData.where((row) {
|
||||
final status = row['status']?.toString().toLowerCase().trim() ?? '';
|
||||
bool _isLoggedInRow(Map<String, dynamic> row) =>
|
||||
row['emp_is_active']?.toString() == '1';
|
||||
|
||||
bool statusMatch;
|
||||
bool _matchesPreStatusFilter(Map<String, dynamic> row, String filter) {
|
||||
final status = row['status']?.toString().toLowerCase().trim() ?? '';
|
||||
final relationship = row['relationship']?.toString().toLowerCase().trim() ?? '';
|
||||
|
||||
if (lowerQuery == 'active' || lowerQuery == 'inactive') {
|
||||
// ✅ Exact match for these two
|
||||
statusMatch = status == lowerQuery;
|
||||
} else {
|
||||
// ✅ Partial match for others
|
||||
statusMatch = status.contains(lowerQuery);
|
||||
}
|
||||
|
||||
return row['name']?.toString().toLowerCase().contains(lowerQuery) ==
|
||||
true ||
|
||||
row['emp_code']
|
||||
?.toString()
|
||||
.toLowerCase()
|
||||
.contains(lowerQuery) ==
|
||||
true ||
|
||||
row['uhid']?.toString().toLowerCase().contains(lowerQuery) ==
|
||||
true ||
|
||||
row['relationship']
|
||||
?.toString()
|
||||
.toLowerCase()
|
||||
.contains(lowerQuery) ==
|
||||
true ||
|
||||
row['formatted_dob']
|
||||
?.toString()
|
||||
.replaceAll("/", "-")
|
||||
.toLowerCase()
|
||||
.contains(lowerQuery) ==
|
||||
true ||
|
||||
row['gender']?.toString().toLowerCase().contains(lowerQuery) ==
|
||||
true ||
|
||||
row['mobile']?.toString().toLowerCase().contains(lowerQuery) ==
|
||||
true ||
|
||||
row['email_corporate']
|
||||
?.toString()
|
||||
.toLowerCase()
|
||||
.contains(lowerQuery) ==
|
||||
true ||
|
||||
statusMatch;
|
||||
}).toList();
|
||||
});
|
||||
switch (filter) {
|
||||
case 'emp_count':
|
||||
return relationship == 'self';
|
||||
case 'enrolled':
|
||||
return _isEnrolledStatus(status);
|
||||
case 'not_enrolled':
|
||||
return !_isEnrolledStatus(status);
|
||||
case 'logged_in':
|
||||
return _isLoggedInRow(row);
|
||||
case 'not_logged_in':
|
||||
return !_isLoggedInRow(row);
|
||||
case 'draft':
|
||||
return status == 'draft';
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// emp_is_active
|
||||
|
||||
bool _matchesSearch(Map<String, dynamic> row, String lowerQuery) {
|
||||
final status = row['status']?.toString().toLowerCase().trim() ?? '';
|
||||
|
||||
bool statusMatch;
|
||||
if (lowerQuery == 'active' || lowerQuery == 'inactive') {
|
||||
statusMatch = status == lowerQuery;
|
||||
} else {
|
||||
statusMatch = status.contains(lowerQuery);
|
||||
}
|
||||
|
||||
return row['name']?.toString().toLowerCase().contains(lowerQuery) == true ||
|
||||
row['emp_code']?.toString().toLowerCase().contains(lowerQuery) ==
|
||||
true ||
|
||||
row['uhid']?.toString().toLowerCase().contains(lowerQuery) == true ||
|
||||
row['relationship']?.toString().toLowerCase().contains(lowerQuery) ==
|
||||
true ||
|
||||
row['formatted_dob']
|
||||
?.toString()
|
||||
.replaceAll("/", "-")
|
||||
.toLowerCase()
|
||||
.contains(lowerQuery) ==
|
||||
true ||
|
||||
row['gender']?.toString().toLowerCase().contains(lowerQuery) == true ||
|
||||
row['mobile']?.toString().toLowerCase().contains(lowerQuery) == true ||
|
||||
row['email_corporate']?.toString().toLowerCase().contains(lowerQuery) ==
|
||||
true ||
|
||||
statusMatch;
|
||||
}
|
||||
|
||||
void _refreshFilteredData() {
|
||||
Iterable<Map<String, dynamic>> data = originalData;
|
||||
|
||||
final lowerQuery = searchController.text.toLowerCase().trim();
|
||||
if (lowerQuery.isNotEmpty) {
|
||||
data = data.where((row) => _matchesSearch(row, lowerQuery));
|
||||
}
|
||||
|
||||
if (localTokenType == 'pre' && _preStatusFilter != null) {
|
||||
data = data.where((row) => _matchesPreStatusFilter(row, _preStatusFilter!));
|
||||
}
|
||||
|
||||
filteredData = data.toList();
|
||||
}
|
||||
|
||||
void search(String query) {
|
||||
setState(() {
|
||||
_currentPage = 1;
|
||||
_refreshFilteredData();
|
||||
});
|
||||
}
|
||||
|
||||
void _onPreStatusFilterTap(String filter) {
|
||||
setState(() {
|
||||
_preStatusFilter = filter;
|
||||
_currentPage = 1;
|
||||
_refreshFilteredData();
|
||||
});
|
||||
}
|
||||
|
||||
void _resetPreStatusFilter() {
|
||||
setState(() {
|
||||
_preStatusFilter = null;
|
||||
_currentPage = 1;
|
||||
_refreshFilteredData();
|
||||
});
|
||||
}
|
||||
|
||||
void exportToCsv(List<Map<String, dynamic>> data) {
|
||||
List<List<String>> rows = [];
|
||||
@ -605,6 +644,260 @@ class _HrPolicyDetailsState extends State<hrPolicyDetails>
|
||||
return value[0].toUpperCase() + value.substring(1).toLowerCase();
|
||||
}
|
||||
|
||||
String _defaultReminderSubject() {
|
||||
final policyName = localCardPolicyName ?? widget.cardPolicy_name;
|
||||
return 'Reminder: Complete Your Enrollment - $policyName';
|
||||
}
|
||||
|
||||
String _defaultReminderBody() {
|
||||
final policyName = localCardPolicyName ?? widget.cardPolicy_name;
|
||||
final policyNo = localCardPolicyNo ?? widget.cardPolicyNo;
|
||||
return '''Dear Employee,
|
||||
|
||||
This is a gentle reminder to complete your insurance enrollment for $policyName ($policyNo).
|
||||
|
||||
Please log in to the Nhance portal and complete your enrollment at the earliest.
|
||||
|
||||
If you have already completed enrollment, please ignore this email.
|
||||
|
||||
Regards,
|
||||
HR Team''';
|
||||
}
|
||||
|
||||
Future<({String subject, String body})> _loadReminderEmailTemplate() async {
|
||||
var subject = _defaultReminderSubject();
|
||||
var body = _defaultReminderBody();
|
||||
|
||||
try {
|
||||
final response = await apiService.getEnrollmentReminderEmailTemplateApi(
|
||||
localClientId ?? widget.ClientId,
|
||||
localClientPolicyId ?? widget.ClientPoliyId,
|
||||
localClientBranchId ?? widget.clientBranchId,
|
||||
localToken ?? widget.Token,
|
||||
);
|
||||
|
||||
if (response['status'] == 'success' || response['status'] == true) {
|
||||
final data = response['data'];
|
||||
if (data is Map<String, dynamic>) {
|
||||
subject = data['email_subject']?.toString().trim().isNotEmpty == true
|
||||
? data['email_subject'].toString()
|
||||
: data['subject']?.toString().trim().isNotEmpty == true
|
||||
? data['subject'].toString()
|
||||
: subject;
|
||||
body = data['email_body']?.toString().trim().isNotEmpty == true
|
||||
? data['email_body'].toString()
|
||||
: data['body']?.toString().trim().isNotEmpty == true
|
||||
? data['body'].toString()
|
||||
: body;
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
logDebug('Reminder template fetch failed, using default: $e');
|
||||
}
|
||||
|
||||
return (subject: subject, body: body);
|
||||
}
|
||||
|
||||
Future<void> _confirmAndSendReminder() async {
|
||||
final template = await _loadReminderEmailTemplate();
|
||||
if (!mounted) return;
|
||||
|
||||
final subjectController = TextEditingController(text: template.subject);
|
||||
final bodyController = TextEditingController(text: template.body);
|
||||
|
||||
final result = await showDialog<Map<String, String>?>(
|
||||
context: context,
|
||||
barrierDismissible: false,
|
||||
builder: (dialogContext) {
|
||||
return AlertDialog(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
),
|
||||
title: Text(
|
||||
'Send Reminder Email',
|
||||
style: GoogleFonts.poppins(
|
||||
fontWeight: FontWeight.w600,
|
||||
fontSize: 18,
|
||||
),
|
||||
),
|
||||
content: SizedBox(
|
||||
width: 620,
|
||||
child: SingleChildScrollView(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'Review and edit the email template before sending to employees who have not completed enrollment.',
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 13,
|
||||
color: Colors.black54,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
'Subject',
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 13,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 6),
|
||||
TextField(
|
||||
controller: subjectController,
|
||||
style: GoogleFonts.poppins(fontSize: 14),
|
||||
decoration: InputDecoration(
|
||||
hintText: 'Email subject',
|
||||
filled: true,
|
||||
fillColor: const Color(0xFFF5F5F5),
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
borderSide: BorderSide.none,
|
||||
),
|
||||
contentPadding: const EdgeInsets.symmetric(
|
||||
horizontal: 12,
|
||||
vertical: 10,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 14),
|
||||
Text(
|
||||
'Email Body',
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 13,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 6),
|
||||
TextField(
|
||||
controller: bodyController,
|
||||
minLines: 10,
|
||||
maxLines: 14,
|
||||
style: GoogleFonts.poppins(fontSize: 14, height: 1.45),
|
||||
decoration: InputDecoration(
|
||||
hintText: 'Email body',
|
||||
filled: true,
|
||||
fillColor: const Color(0xFFF5F5F5),
|
||||
alignLabelWithHint: true,
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
borderSide: BorderSide.none,
|
||||
),
|
||||
contentPadding: const EdgeInsets.all(12),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(dialogContext),
|
||||
child: Text(
|
||||
'Cancel',
|
||||
style: GoogleFonts.poppins(color: Colors.black54),
|
||||
),
|
||||
),
|
||||
ElevatedButton(
|
||||
onPressed: () {
|
||||
final subject = subjectController.text.trim();
|
||||
final body = bodyController.text.trim();
|
||||
if (subject.isEmpty || body.isEmpty) {
|
||||
ToastHelper.showWarningToast(
|
||||
dialogContext,
|
||||
'Subject and email body are required',
|
||||
);
|
||||
return;
|
||||
}
|
||||
Navigator.pop(dialogContext, {
|
||||
'subject': subject,
|
||||
'body': body,
|
||||
});
|
||||
},
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: const Color(0xFFE26728),
|
||||
),
|
||||
child: Text(
|
||||
'Send',
|
||||
style: GoogleFonts.poppins(color: Colors.white),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
subjectController.dispose();
|
||||
bodyController.dispose();
|
||||
|
||||
if (result != null) {
|
||||
await _sendEnrollmentReminder(
|
||||
emailSubject: result['subject']!,
|
||||
emailBody: result['body']!,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _sendEnrollmentReminder({
|
||||
required String emailSubject,
|
||||
required String emailBody,
|
||||
}) async {
|
||||
setState(() => _isSendingReminder = true);
|
||||
|
||||
try {
|
||||
final hrId = await tokenService.readValue('enrollmentHrId');
|
||||
final response = await apiService.sendEnrollmentReminderApi(
|
||||
localClientId ?? widget.ClientId,
|
||||
localClientPolicyId ?? widget.ClientPoliyId,
|
||||
localClientBranchId ?? widget.clientBranchId,
|
||||
hrId ?? '',
|
||||
localToken ?? widget.Token,
|
||||
emailSubject: emailSubject,
|
||||
emailBody: emailBody,
|
||||
);
|
||||
|
||||
final ok = response['status'] == 'success' || response['status'] == true;
|
||||
if (ok) {
|
||||
final message = response['message']?.toString() ??
|
||||
'Reminder sent successfully';
|
||||
ToastHelper.showSuccessToast(context, message);
|
||||
await _logReminderActivity();
|
||||
} else {
|
||||
ToastHelper.showErrorToast(
|
||||
context,
|
||||
response['message']?.toString() ?? 'Failed to send reminder',
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
logDebug('Reminder exception: $e');
|
||||
ToastHelper.showErrorToast(
|
||||
context,
|
||||
'Failed to send reminder. Please try again.',
|
||||
);
|
||||
} finally {
|
||||
if (mounted) {
|
||||
setState(() => _isSendingReminder = false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _logReminderActivity() async {
|
||||
final postId = await tokenService.readValue('empHrId');
|
||||
final preId = await tokenService.readValue('enrollmentEmpPrimaryId');
|
||||
const activityPre = 'send_enrollment_reminder';
|
||||
|
||||
try {
|
||||
await apiService.getPreLogHrActivity(
|
||||
postId!,
|
||||
preId!,
|
||||
localToken!,
|
||||
activityPre,
|
||||
);
|
||||
} catch (e) {
|
||||
logDebug('Reminder activity log failed: $e');
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> getEcardBulkDownload() async {
|
||||
try {
|
||||
final emp_policy_ids = selectedEmployeeIds.toList();
|
||||
@ -810,6 +1103,48 @@ class _HrPolicyDetailsState extends State<hrPolicyDetails>
|
||||
|
||||
const SizedBox(width: 12),
|
||||
|
||||
if (localTokenType == 'pre') ...[
|
||||
SizedBox(
|
||||
width: 142,
|
||||
height: 37,
|
||||
child: ElevatedButton(
|
||||
onPressed: _isSendingReminder
|
||||
? null
|
||||
: _confirmAndSendReminder,
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: const Color(0xFFE26728),
|
||||
elevation: 0,
|
||||
padding: EdgeInsets.zero,
|
||||
disabledBackgroundColor:
|
||||
const Color(0xFFE26728).withValues(alpha: 0.6),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
),
|
||||
child: _isSendingReminder
|
||||
? const SizedBox(
|
||||
width: 18,
|
||||
height: 18,
|
||||
child: CircularProgressIndicator(
|
||||
strokeWidth: 2,
|
||||
color: Colors.white,
|
||||
),
|
||||
)
|
||||
: Text(
|
||||
'Reminder',
|
||||
maxLines: 1,
|
||||
softWrap: false,
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 15,
|
||||
fontWeight: FontWeight.w700,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
],
|
||||
|
||||
SizedBox(
|
||||
width: 116,
|
||||
height: 37,
|
||||
@ -1339,60 +1674,158 @@ class _HrPolicyDetailsState extends State<hrPolicyDetails>
|
||||
// );
|
||||
// }
|
||||
|
||||
Map<String, int> getFixedStatusCounts() {
|
||||
int draftCount = 0;
|
||||
int enrolledCount = 0;
|
||||
Map<String, int> getPreStatusCounts() {
|
||||
int empCount = 0;
|
||||
int enrolled = 0;
|
||||
int notEnrolled = 0;
|
||||
int loggedIn = 0;
|
||||
int notLoggedIn = 0;
|
||||
int draft = 0;
|
||||
|
||||
for (final item in filteredData) {
|
||||
final status = item['status']?.toString().toLowerCase();
|
||||
for (final item in originalData) {
|
||||
final status = item['status']?.toString().toLowerCase().trim() ?? '';
|
||||
final relationship =
|
||||
item['relationship']?.toString().toLowerCase().trim() ?? '';
|
||||
|
||||
if (status == 'draft') {
|
||||
draftCount++;
|
||||
} else if (status == 'under process') {
|
||||
enrolledCount++;
|
||||
if (relationship == 'self') empCount++;
|
||||
if (_isEnrolledStatus(status)) {
|
||||
enrolled++;
|
||||
} else {
|
||||
notEnrolled++;
|
||||
}
|
||||
if (_isLoggedInRow(item)) {
|
||||
loggedIn++;
|
||||
} else {
|
||||
notLoggedIn++;
|
||||
}
|
||||
if (status == 'draft') draft++;
|
||||
}
|
||||
|
||||
return {
|
||||
'draft': draftCount,
|
||||
'under process': enrolledCount,
|
||||
'total': filteredData.length,
|
||||
'emp_count': empCount,
|
||||
'enrolled': enrolled,
|
||||
'not_enrolled': notEnrolled,
|
||||
'logged_in': loggedIn,
|
||||
'not_logged_in': notLoggedIn,
|
||||
'draft': draft,
|
||||
};
|
||||
}
|
||||
|
||||
Color _getPreFilterChipColor(String filter) {
|
||||
switch (filter) {
|
||||
case 'emp_count':
|
||||
return const Color(0xFFE2FBCB);
|
||||
case 'enrolled':
|
||||
return const Color(0xFFBDF9D9);
|
||||
case 'not_enrolled':
|
||||
return const Color(0xFFFFE8AC);
|
||||
case 'logged_in':
|
||||
return const Color(0xFFC5F2F4);
|
||||
case 'not_logged_in':
|
||||
return const Color(0xFFE8EAF6);
|
||||
case 'draft':
|
||||
return const Color(0xFFF9EBBD);
|
||||
default:
|
||||
return const Color(0xFFB0BEC5);
|
||||
}
|
||||
}
|
||||
|
||||
Widget _buildPreStatusFilterChip({
|
||||
required String filterKey,
|
||||
required String label,
|
||||
required int count,
|
||||
}) {
|
||||
final selected = _preStatusFilter == filterKey;
|
||||
final color = _getPreFilterChipColor(filterKey);
|
||||
|
||||
return Material(
|
||||
color: Colors.transparent,
|
||||
child: InkWell(
|
||||
onTap: () => _onPreStatusFilterTap(filterKey),
|
||||
borderRadius: BorderRadius.circular(6),
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
|
||||
decoration: BoxDecoration(
|
||||
color: color,
|
||||
borderRadius: BorderRadius.circular(6),
|
||||
border: Border.all(
|
||||
color: selected ? const Color(0xFF009195) : Colors.transparent,
|
||||
width: 2,
|
||||
),
|
||||
),
|
||||
child: Text(
|
||||
'$label - $count',
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 12,
|
||||
fontWeight: selected ? FontWeight.w700 : FontWeight.w600,
|
||||
color: Colors.black,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildStatusSummary() {
|
||||
if (filteredData.isEmpty) {
|
||||
if (originalData.isEmpty) {
|
||||
return const SizedBox();
|
||||
}
|
||||
|
||||
final statusCounts = getFixedStatusCounts();
|
||||
final List<String> order = ['draft', 'under process', 'total'];
|
||||
final statusCounts = getPreStatusCounts();
|
||||
const filters = <Map<String, String>>[
|
||||
{'key': 'emp_count', 'label': 'Emp Count'},
|
||||
{'key': 'enrolled', 'label': 'Enroled'},
|
||||
{'key': 'not_enrolled', 'label': 'Not Enroled'},
|
||||
{'key': 'logged_in', 'label': 'Logged-In'},
|
||||
{'key': 'not_logged_in', 'label': 'Not Logged-In'},
|
||||
{'key': 'draft', 'label': 'Draft'},
|
||||
];
|
||||
|
||||
return SizedBox(
|
||||
height: 34,
|
||||
height: 38,
|
||||
child: ListView.separated(
|
||||
scrollDirection: Axis.horizontal,
|
||||
itemCount: order.length,
|
||||
itemCount: filters.length + 1,
|
||||
separatorBuilder: (_, __) => const SizedBox(width: 10),
|
||||
itemBuilder: (context, index) {
|
||||
final status = order[index];
|
||||
final count = statusCounts[status] ?? 0;
|
||||
final color = getStatusColor(status);
|
||||
|
||||
return Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
|
||||
decoration: BoxDecoration(
|
||||
color: color,
|
||||
borderRadius: BorderRadius.circular(6),
|
||||
),
|
||||
child: Text(
|
||||
'${status.toUpperCase()} - $count',
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Colors.black,
|
||||
if (index == filters.length) {
|
||||
return Material(
|
||||
color: Colors.transparent,
|
||||
child: InkWell(
|
||||
onTap: _preStatusFilter == null ? null : _resetPreStatusFilter,
|
||||
borderRadius: BorderRadius.circular(6),
|
||||
child: Container(
|
||||
padding:
|
||||
const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFFF0F0F0),
|
||||
borderRadius: BorderRadius.circular(6),
|
||||
border: Border.all(
|
||||
color: _preStatusFilter != null
|
||||
? const Color(0xFF009195)
|
||||
: const Color(0xFFD0D0D0),
|
||||
),
|
||||
),
|
||||
child: Text(
|
||||
'Reset',
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: _preStatusFilter != null
|
||||
? const Color(0xFF009195)
|
||||
: Colors.black54,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
final filter = filters[index];
|
||||
return _buildPreStatusFilterChip(
|
||||
filterKey: filter['key']!,
|
||||
label: filter['label']!,
|
||||
count: statusCounts[filter['key']] ?? 0,
|
||||
);
|
||||
},
|
||||
),
|
||||
|
||||
@ -1028,6 +1028,66 @@ class ApiService {
|
||||
// return response;
|
||||
// }
|
||||
|
||||
Future<Map<String, dynamic>> getEnrollmentReminderEmailTemplateApi(
|
||||
String clientId,
|
||||
String clientPolicyId,
|
||||
String clientBranchId,
|
||||
String token,
|
||||
) async {
|
||||
final url = Uri.parse(
|
||||
'${Environment.apiUrl}getEnrollmentReminderEmailTemplate'
|
||||
'?client_id=$clientId&client_policy_id=$clientPolicyId'
|
||||
'&client_branch_id=$clientBranchId',
|
||||
);
|
||||
final headers = {
|
||||
'Authorization': 'Bearer ${token ?? ''}',
|
||||
'APP-SIGNATURE':
|
||||
'nhance-2025-signature-T8f6gV9k5fK2wf5V5c4vId3b0J8z6cAm2x8Y',
|
||||
};
|
||||
return _makeGetRequest(url, headers);
|
||||
}
|
||||
|
||||
Future<Map<String, dynamic>> sendEnrollmentReminderApi(
|
||||
String clientId,
|
||||
String clientPolicyId,
|
||||
String clientBranchId,
|
||||
String hrId,
|
||||
String token, {
|
||||
required String emailSubject,
|
||||
required String emailBody,
|
||||
}) async {
|
||||
final url = Uri.parse('${Environment.apiUrl}sendEnrollmentReminderEmail');
|
||||
|
||||
final headers = {
|
||||
'Authorization': 'Bearer ${token ?? ''}',
|
||||
'APP-SIGNATURE':
|
||||
'nhance-2025-signature-T8f6gV9k5fK2wf5V5c4vId3b0J8z6cAm2x8Y',
|
||||
'Content-Type': 'application/json',
|
||||
};
|
||||
|
||||
final body = {
|
||||
'client_id': clientId,
|
||||
'client_policy_id': clientPolicyId,
|
||||
'client_branch_id': clientBranchId,
|
||||
'hr_id': hrId,
|
||||
'email_subject': emailSubject,
|
||||
'email_body': emailBody,
|
||||
};
|
||||
|
||||
final response = await http.post(
|
||||
url,
|
||||
headers: headers,
|
||||
body: jsonEncode(body),
|
||||
);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
return jsonDecode(response.body);
|
||||
} else {
|
||||
throw Exception(
|
||||
'Failed to send enrollment reminder: ${response.statusCode} ${response.body}');
|
||||
}
|
||||
}
|
||||
|
||||
Future<Map<String, dynamic>> getEmployeeAndDependenceToApiPre(
|
||||
String clintID, String getPolicyNo, String empRefId, String token) async {
|
||||
logDebug(_hrtoken);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user