approvals screen done

This commit is contained in:
Surendiran 2026-08-05 09:51:51 +05:30
parent b8bd8952ed
commit 7ce3e95b17
6 changed files with 1177 additions and 12 deletions

View File

@ -54,6 +54,8 @@ class Environment {
return "$base - Dashboard";
case 'policies':
return "$base - Policies";
case 'pendingDependentApproval':
return "$base - Approvals";
case 'CdPoliciesList':
return "$base - CD Details";
case 'ClaimsPolicies':

View File

@ -89,6 +89,12 @@ class _NhanceSideBarState extends State<NhanceSideBar> {
'label': 'Policies',
'icon': 'policies',
});
items.add({
'route': 'pendingDependentApproval',
'label': 'Approvals',
'icon': 'pending_approve',
'useMaterialIcon': true,
});
}
// CD
@ -363,18 +369,25 @@ class _NhanceSideBarState extends State<NhanceSideBar> {
final showClaimsSubMenu = isClaims && _hasMultipleClaimsSubMenus;
final claimsDirectRoute =
isClaims ? (_singleClaimsRoute ?? 'ClaimsPolicies') : null;
final useMaterialIcon = item['useMaterialIcon'] == true;
return _SideItem(
key: itemKey,
icon: SvgPicture.string(
SvgService.getSvg(item['icon']),
width: 35,
height: 35,
colorFilter: const ColorFilter.mode(
Colors.white,
BlendMode.srcIn,
),
),
icon: useMaterialIcon
? const Icon(
Icons.how_to_reg_outlined,
color: Colors.white,
size: 30,
)
: SvgPicture.string(
SvgService.getSvg(item['icon']),
width: 35,
height: 35,
colorFilter: const ColorFilter.mode(
Colors.white,
BlendMode.srcIn,
),
),
label: item['label'],
isActive: isClaims ? isClaimsActive : activeRoute == item['route'],
onTap: showClaimsSubMenu
@ -730,10 +743,14 @@ class _SideItem extends StatelessWidget {
const SizedBox(height: 6),
Text(
label,
textAlign: TextAlign.center,
maxLines: 2,
overflow: TextOverflow.ellipsis,
style: const TextStyle(
color: Colors.white,
fontSize: 11,
fontSize: 10,
fontWeight: FontWeight.w500,
height: 1.15,
),
),
],

View File

@ -12,6 +12,7 @@ import 'package:nhancepolicy/presentation/postFileUpload.dart';
import 'package:nhancepolicy/presentation/cdList.dart';
import 'package:nhancepolicy/presentation/claims.dart';
import 'package:nhancepolicy/presentation/nonEBClaimsList.dart';
import 'package:nhancepolicy/presentation/pendingDependentApproval.dart';
import 'package:nhancepolicy/presentation/policies.dart';
import 'package:nhancepolicy/service/session/session_service.dart';
import 'package:nhancepolicy/service/token_storage_service.dart';
@ -187,6 +188,7 @@ const Set<String> _authProtectedRoutes = {
'hrHome',
'hrDashboard',
'policies',
'pendingDependentApproval',
'claimsOverviewDashboard',
'ClaimsPolicies',
'nonEBClaimsList',
@ -378,6 +380,9 @@ final Map<String, WidgetBuilder> appRoutes = {
'policies': (context) => AuthWrapper(
builder: (_) => const policies(),
),
'pendingDependentApproval': (context) => AuthWrapper(
builder: (_) => const PendingDependentApproval(),
),
'CdPoliciesList': (context) => AuthWrapper(
builder: (_) => CdPoliciesList(),
),

View File

@ -1947,13 +1947,13 @@ class _HrPolicyDetailsState extends State<hrPolicyDetails>
],
(
label: 'Import',
icon: Icons.upload_file_rounded,
icon: Icons.file_download_outlined,
color: const Color(0xFFE26728),
onTap: _openImportFlow,
),
(
label: 'Export',
icon: Icons.download_rounded,
icon: Icons.file_upload_outlined,
color: const Color(0xFFE26728),
onTap: localTokenType == 'pre'
? null

File diff suppressed because it is too large Load Diff

View File

@ -750,6 +750,120 @@ class ApiService {
return _makeGetRequest(url, headers);
}
/// Pending approval dependents list
/// (`GET getPendingApprovalDependents`) post API only.
Future<Map<String, dynamic>> getPendingApprovalDependents({
String? clientId,
String? branchId,
String? search,
String? token,
}) async {
final authToken = token ?? _token;
if (authToken == null || authToken.isEmpty) {
await _initializeToken();
}
final query = <String, String>{};
if (clientId != null && clientId.toString().trim().isNotEmpty) {
query['client_id'] = clientId.toString();
}
if (branchId != null && branchId.toString().trim().isNotEmpty) {
query['client_branch_id'] = branchId.toString();
query['branch_id'] = branchId.toString();
}
if (search != null && search.trim().isNotEmpty) {
query['search'] = search.trim();
}
final url = Uri.parse(
'${Environment.apiUrlPost}getPendingApprovalDependents',
).replace(
queryParameters: query.isEmpty ? null : query,
);
final headers = {
'Authorization': 'Bearer ${token ?? _token ?? ''}',
'APP-SIGNATURE':
'nhance-2025-signature-T8f6gV9k5fK2wf5V5c4vId3b0J8z6cAm2x8Y',
};
final response = await http.get(url, headers: headers);
if (response.statusCode == 200) {
return _parseJsonBody(response);
}
// API returns 404 when there are no pending dependents.
if (response.statusCode == 404) {
final body = _parseJsonBody(response);
return {
'status': 'success',
'code': 404,
'message': body['message'] ?? 'No pending approval dependents found',
'data': <dynamic>[],
};
}
return _handleResponse(response);
}
/// Approve or reject a pending dependent
/// (`POST processDependentAdd`).
Future<Map<String, dynamic>> processDependentAdd({
required bool isPost,
required dynamic employeeId,
required dynamic clientPolicyId,
required String status,
dynamic hrId,
String? rejectReason,
String? token,
}) async {
final authToken = token ?? _token;
if (authToken == null || authToken.isEmpty) {
await _initializeToken();
}
final baseUrl = isPost ? Environment.apiUrlPost : Environment.apiUrl;
final url = Uri.parse('${baseUrl}processDependentAdd');
final headers = {
'Authorization': 'Bearer ${token ?? _token ?? ''}',
'Content-Type': 'application/json',
'APP-SIGNATURE':
'nhance-2025-signature-T8f6gV9k5fK2wf5V5c4vId3b0J8z6cAm2x8Y',
};
final body = <String, dynamic>{
'employee_id': employeeId,
'client_policy_id': clientPolicyId,
'status': status,
};
if (hrId != null && hrId.toString().trim().isNotEmpty) {
body['hr_id'] = hrId is num ? hrId : int.tryParse(hrId.toString()) ?? hrId;
}
if (status == 'rejected' &&
rejectReason != null &&
rejectReason.trim().isNotEmpty) {
body['reject_reason'] = rejectReason.trim();
}
final response = await http.post(
url,
headers: headers,
body: jsonEncode(body),
);
if (response.statusCode == 200) {
return _parseJsonBody(response);
}
// Surface API validation messages (400/404) without session redirect.
if (response.statusCode == 400 || response.statusCode == 404) {
final parsed = _parseJsonBody(response);
return {
'status': parsed['status'] ?? 'error',
'code': response.statusCode,
'message': parsed['message'] ??
parsed['data']?.toString() ??
'Request failed',
'data': parsed['data'],
};
}
return _handleResponse(response);
}
/// Excel export for employee list
/// (`downloadEmployeeListExcel?client_id=&policy_id=&branch_id=`).
Future<http.Response> downloadEmployeeListExcel({