changes done
This commit is contained in:
parent
75e5baf0fd
commit
1e97c41545
@ -32,6 +32,7 @@ class _NhanceSideBarState extends State<NhanceSideBar> {
|
|||||||
List<Map<String, dynamic>> sideMenuItems = [];
|
List<Map<String, dynamic>> sideMenuItems = [];
|
||||||
List<int> postModules = [];
|
List<int> postModules = [];
|
||||||
List<int> enrollmentModules = [];
|
List<int> enrollmentModules = [];
|
||||||
|
List<String> claimsSubMenu = [];
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
@ -59,8 +60,26 @@ class _NhanceSideBarState extends State<NhanceSideBar> {
|
|||||||
? List<int>.from(jsonDecode(postRaw))
|
? List<int>.from(jsonDecode(postRaw))
|
||||||
: [];
|
: [];
|
||||||
|
|
||||||
|
final claimsSubMenuRaw = await tokenService.readValue('claims_sub_menu');
|
||||||
|
if (claimsSubMenuRaw != null && claimsSubMenuRaw.isNotEmpty) {
|
||||||
|
claimsSubMenu = List<dynamic>.from(jsonDecode(claimsSubMenuRaw))
|
||||||
|
.map((e) => e.toString())
|
||||||
|
.toList();
|
||||||
|
} else {
|
||||||
|
// Fallback: decoded JWT / selected branch may still have the list.
|
||||||
|
final decoded = tokenService.getDecodedToken();
|
||||||
|
final branch = tokenService.getSelectedBranch();
|
||||||
|
final fromDecoded = decoded?['claims_sub_menu'];
|
||||||
|
final fromBranch = branch?['claims_sub_menu'];
|
||||||
|
final source = fromBranch is List
|
||||||
|
? fromBranch
|
||||||
|
: (fromDecoded is List ? fromDecoded : const []);
|
||||||
|
claimsSubMenu = source.map((e) => e.toString()).toList();
|
||||||
|
}
|
||||||
|
|
||||||
logDebug('enrollmentModules $enrollmentModules');
|
logDebug('enrollmentModules $enrollmentModules');
|
||||||
logDebug('postModules $postModules');
|
logDebug('postModules $postModules');
|
||||||
|
logDebug('claimsSubMenu $claimsSubMenu');
|
||||||
|
|
||||||
final List<Map<String, dynamic>> items = [];
|
final List<Map<String, dynamic>> items = [];
|
||||||
|
|
||||||
@ -82,8 +101,9 @@ class _NhanceSideBarState extends State<NhanceSideBar> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// CLAIMS
|
// CLAIMS — only when module 4 and at least one sub-menu is allowed
|
||||||
if (postModules.contains(4)) {
|
if (postModules.contains(4) &&
|
||||||
|
(claimsSubMenu.contains('EB') || claimsSubMenu.contains('Non-EB'))) {
|
||||||
items.add({
|
items.add({
|
||||||
'route': 'ClaimsPolicies',
|
'route': 'ClaimsPolicies',
|
||||||
'label': 'Claims',
|
'label': 'Claims',
|
||||||
@ -173,8 +193,10 @@ class _NhanceSideBarState extends State<NhanceSideBar> {
|
|||||||
child: Column(
|
child: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
_buildClaimsMenuItem('EB', 'ClaimsPolicies'),
|
if (claimsSubMenu.contains('EB'))
|
||||||
_buildClaimsMenuItem('Non-EB', 'nonEBClaimsList'),
|
_buildClaimsMenuItem('EB', 'ClaimsPolicies'),
|
||||||
|
if (claimsSubMenu.contains('Non-EB'))
|
||||||
|
_buildClaimsMenuItem('Non-EB', 'nonEBClaimsList'),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
278
lib/main.dart
278
lib/main.dart
@ -181,6 +181,52 @@ Future<void> startApp() async {
|
|||||||
runApp(MyApp(initialToken: token));
|
runApp(MyApp(initialToken: token));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Routes that require an authenticated HR session.
|
||||||
|
/// Public auth-flow routes (hrLogin, mailVerify, branchSelection) stay excluded.
|
||||||
|
const Set<String> _authProtectedRoutes = {
|
||||||
|
'hrHome',
|
||||||
|
'hrDashboard',
|
||||||
|
'policies',
|
||||||
|
'claimsOverviewDashboard',
|
||||||
|
'ClaimsPolicies',
|
||||||
|
'nonEBClaimsList',
|
||||||
|
'CdPoliciesList',
|
||||||
|
'cdTransactionDetails',
|
||||||
|
'hrPolicyDetails',
|
||||||
|
'oldPolicy',
|
||||||
|
'preFileUpload',
|
||||||
|
'postFileUpload',
|
||||||
|
'excelErrorScreen',
|
||||||
|
'empDetails',
|
||||||
|
'addOnsDetails',
|
||||||
|
'empReviewDetails',
|
||||||
|
};
|
||||||
|
|
||||||
|
String _normalizeRouteName(String routeName) {
|
||||||
|
var name = routeName.trim();
|
||||||
|
if (name.startsWith('#')) {
|
||||||
|
name = name.substring(1);
|
||||||
|
}
|
||||||
|
if (name.startsWith('/')) {
|
||||||
|
name = name.substring(1);
|
||||||
|
}
|
||||||
|
// Flutter web may include query/hash fragments; keep only path segment.
|
||||||
|
final hashIndex = name.indexOf('#');
|
||||||
|
if (hashIndex != -1) {
|
||||||
|
name = name.substring(hashIndex + 1);
|
||||||
|
}
|
||||||
|
final queryIndex = name.indexOf('?');
|
||||||
|
if (queryIndex != -1) {
|
||||||
|
name = name.substring(0, queryIndex);
|
||||||
|
}
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool _hasValidSession() {
|
||||||
|
final token = TokenStorageService().getCurrentToken();
|
||||||
|
return token != null && token.trim().isNotEmpty;
|
||||||
|
}
|
||||||
|
|
||||||
class MyApp extends StatelessWidget {
|
class MyApp extends StatelessWidget {
|
||||||
final String? initialToken;
|
final String? initialToken;
|
||||||
|
|
||||||
@ -188,12 +234,33 @@ class MyApp extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
final fallbackRoute =
|
||||||
|
(initialToken == null || initialToken!.isEmpty) ? 'hrLogin' : 'hrHome';
|
||||||
|
|
||||||
return MaterialApp(
|
return MaterialApp(
|
||||||
title: 'Nhance HR',
|
title: 'Nhance HR',
|
||||||
debugShowCheckedModeBanner: false,
|
debugShowCheckedModeBanner: false,
|
||||||
initialRoute: (initialToken == null || initialToken!.isEmpty)
|
initialRoute: fallbackRoute,
|
||||||
? 'hrLogin'
|
onGenerateInitialRoutes: (String initialRouteName) {
|
||||||
: 'hrHome',
|
final requested = _normalizeRouteName(initialRouteName);
|
||||||
|
final resolved = (requested.isEmpty || requested == '/')
|
||||||
|
? fallbackRoute
|
||||||
|
: requested;
|
||||||
|
|
||||||
|
// Deep-link guard: unauthenticated access to protected URLs → HR login
|
||||||
|
final targetRoute =
|
||||||
|
(!_hasValidSession() && _authProtectedRoutes.contains(resolved))
|
||||||
|
? 'hrLogin'
|
||||||
|
: resolved;
|
||||||
|
|
||||||
|
final builder = appRoutes[targetRoute] ?? appRoutes['hrLogin']!;
|
||||||
|
return [
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: builder,
|
||||||
|
settings: RouteSettings(name: targetRoute),
|
||||||
|
),
|
||||||
|
];
|
||||||
|
},
|
||||||
theme: ThemeData(
|
theme: ThemeData(
|
||||||
primaryColor: const Color(0xFF00999E),
|
primaryColor: const Color(0xFF00999E),
|
||||||
scaffoldBackgroundColor: Colors.white,
|
scaffoldBackgroundColor: Colors.white,
|
||||||
@ -225,110 +292,159 @@ final Map<String, WidgetBuilder> appRoutes = {
|
|||||||
// resendToken: null,
|
// resendToken: null,
|
||||||
// onResendCode: (String, int) {},
|
// onResendCode: (String, int) {},
|
||||||
// ),
|
// ),
|
||||||
'hrHome': (context) => MyHrHome(),
|
'hrHome': (context) => AuthWrapper(
|
||||||
'preFileUpload': (context) => const preFileUpload(
|
builder: (_) => MyHrHome(),
|
||||||
ClientId: '',
|
|
||||||
policyTypeId: '',
|
|
||||||
ClientPoliyId: '',
|
|
||||||
clientBranchId: '',
|
|
||||||
Token: '',
|
|
||||||
TokenType: '',
|
|
||||||
cardType: '',
|
|
||||||
cardPolicyNo: '',
|
|
||||||
cardInsurer_name: '',
|
|
||||||
cardPolicy_name: '',
|
|
||||||
cardPolicy_ExpDate: '',
|
|
||||||
total_premium: '',
|
|
||||||
),
|
),
|
||||||
'postFileUpload': (context) => const postFileUpload(
|
'preFileUpload': (context) => AuthWrapper(
|
||||||
ClientId: '',
|
builder: (_) => const preFileUpload(
|
||||||
policyTypeId: '',
|
ClientId: '',
|
||||||
ClientPoliyId: '',
|
policyTypeId: '',
|
||||||
clientBranchId: '',
|
ClientPoliyId: '',
|
||||||
Token: '',
|
clientBranchId: '',
|
||||||
TokenType: '',
|
Token: '',
|
||||||
cardType: '',
|
TokenType: '',
|
||||||
cardPolicyNo: '',
|
cardType: '',
|
||||||
cardInsurer_name: '',
|
cardPolicyNo: '',
|
||||||
cardPolicy_name: '',
|
cardInsurer_name: '',
|
||||||
cardPolicy_ExpDate: '',
|
cardPolicy_name: '',
|
||||||
total_premium: '',
|
cardPolicy_ExpDate: '',
|
||||||
|
total_premium: '',
|
||||||
|
),
|
||||||
),
|
),
|
||||||
'excelErrorScreen': (context) => const excelErrorScreen(
|
'postFileUpload': (context) => AuthWrapper(
|
||||||
ClientId: '',
|
builder: (_) => const postFileUpload(
|
||||||
policy_no: '',
|
ClientId: '',
|
||||||
action: '',
|
policyTypeId: '',
|
||||||
created_at: '',
|
ClientPoliyId: '',
|
||||||
clientBranchId: '',
|
clientBranchId: '',
|
||||||
Token: '',
|
Token: '',
|
||||||
TokenType: '',
|
TokenType: '',
|
||||||
id: ''),
|
cardType: '',
|
||||||
'empDetails': (context) => empDetails(),
|
cardPolicyNo: '',
|
||||||
'addOnsDetails': (context) => addOnsDetails(),
|
cardInsurer_name: '',
|
||||||
'empReviewDetails': (context) => empReviewDetails(),
|
cardPolicy_name: '',
|
||||||
'hrDashboard': (context) => policies(),
|
cardPolicy_ExpDate: '',
|
||||||
'claimsOverviewDashboard': (context) => const ClaimsOverviewDashboard(),
|
total_premium: '',
|
||||||
'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(),
|
'excelErrorScreen': (context) => AuthWrapper(
|
||||||
|
builder: (_) => const excelErrorScreen(
|
||||||
|
ClientId: '',
|
||||||
|
policy_no: '',
|
||||||
|
action: '',
|
||||||
|
created_at: '',
|
||||||
|
clientBranchId: '',
|
||||||
|
Token: '',
|
||||||
|
TokenType: '',
|
||||||
|
id: ''),
|
||||||
|
),
|
||||||
|
'empDetails': (context) => AuthWrapper(
|
||||||
|
builder: (_) => empDetails(),
|
||||||
|
),
|
||||||
|
'addOnsDetails': (context) => AuthWrapper(
|
||||||
|
builder: (_) => addOnsDetails(),
|
||||||
|
),
|
||||||
|
'empReviewDetails': (context) => AuthWrapper(
|
||||||
|
builder: (_) => empReviewDetails(),
|
||||||
|
),
|
||||||
|
'hrDashboard': (context) => AuthWrapper(
|
||||||
|
builder: (_) => const policies(),
|
||||||
|
),
|
||||||
|
'claimsOverviewDashboard': (context) => AuthWrapper(
|
||||||
|
builder: (_) => const ClaimsOverviewDashboard(),
|
||||||
|
),
|
||||||
|
'hrPolicyDetails': (context) => AuthWrapper(
|
||||||
|
builder: (_) => 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) => AuthWrapper(
|
||||||
|
builder: (_) => oldPolicy(),
|
||||||
|
),
|
||||||
|
// Branch selection happens after login but before a branch token is set.
|
||||||
'branchSelection': (context) => BranchSelectionPage(),
|
'branchSelection': (context) => BranchSelectionPage(),
|
||||||
'policies': (context) => policies(),
|
'policies': (context) => AuthWrapper(
|
||||||
'CdPoliciesList': (context) => CdPoliciesList(),
|
builder: (_) => const policies(),
|
||||||
'ClaimsPolicies': (context) => ClaimsPolicies(
|
|
||||||
empCode: '',
|
|
||||||
),
|
),
|
||||||
'nonEBClaimsList': (context) => const NonEBClaimsList(empCode: '',),
|
'CdPoliciesList': (context) => AuthWrapper(
|
||||||
'cdTransactionDetails': (context) => cdTransactionDetails(
|
builder: (_) => CdPoliciesList(),
|
||||||
insurerName: '',
|
),
|
||||||
cdMasterAccountNo: '',
|
'ClaimsPolicies': (context) => AuthWrapper(
|
||||||
insurerId: '',
|
builder: (_) => const ClaimsPolicies(
|
||||||
cd_ac_pk: '',
|
empCode: '',
|
||||||
empClientId: '',
|
),
|
||||||
|
),
|
||||||
|
'nonEBClaimsList': (context) => AuthWrapper(
|
||||||
|
builder: (_) => const NonEBClaimsList(
|
||||||
|
empCode: '',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
'cdTransactionDetails': (context) => AuthWrapper(
|
||||||
|
builder: (_) => cdTransactionDetails(
|
||||||
|
insurerName: '',
|
||||||
|
cdMasterAccountNo: '',
|
||||||
|
insurerId: '',
|
||||||
|
cd_ac_pk: '',
|
||||||
|
empClientId: '',
|
||||||
|
),
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
|
|
||||||
/// 🔐 Global Auth Wrapper (Protects All Pages)
|
/// 🔐 Auth Wrapper — blocks protected pages until a valid session exists.
|
||||||
|
/// Uses a builder so protected widgets are not constructed without a token.
|
||||||
class AuthWrapper extends StatefulWidget {
|
class AuthWrapper extends StatefulWidget {
|
||||||
final Widget child;
|
final WidgetBuilder builder;
|
||||||
|
|
||||||
const AuthWrapper({super.key, required this.child});
|
const AuthWrapper({super.key, required this.builder});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<AuthWrapper> createState() => _AuthWrapperState();
|
State<AuthWrapper> createState() => _AuthWrapperState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _AuthWrapperState extends State<AuthWrapper> {
|
class _AuthWrapperState extends State<AuthWrapper> {
|
||||||
|
late final bool _isAuthenticated;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
_checkLogin();
|
_isAuthenticated = _hasValidSession();
|
||||||
}
|
|
||||||
|
|
||||||
void _checkLogin() {
|
if (!_isAuthenticated) {
|
||||||
final token = TokenStorageService().getCurrentToken();
|
|
||||||
|
|
||||||
if (token == null || token.isEmpty) {
|
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
Navigator.pushNamedAndRemoveUntil(context, 'hrLogin', (route) => false);
|
if (!mounted) return;
|
||||||
|
Navigator.pushNamedAndRemoveUntil(
|
||||||
|
context,
|
||||||
|
'hrLogin',
|
||||||
|
(route) => false,
|
||||||
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return widget.child;
|
if (!_isAuthenticated) {
|
||||||
|
// Do not render protected UI while redirecting to login.
|
||||||
|
return const Scaffold(
|
||||||
|
body: Center(
|
||||||
|
child: CircularProgressIndicator(
|
||||||
|
color: Color(0xFF00999E),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return widget.builder(context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,6 +15,7 @@ import 'package:universal_html/html.dart' as html;
|
|||||||
import '../service/secure_pop_scope.dart';
|
import '../service/secure_pop_scope.dart';
|
||||||
import 'claimshistory.dart';
|
import 'claimshistory.dart';
|
||||||
import '../customAppBar/base_layout.dart';
|
import '../customAppBar/base_layout.dart';
|
||||||
|
import '../customAppBar/toastHelper.dart';
|
||||||
import '../service/api_service.dart';
|
import '../service/api_service.dart';
|
||||||
import '../service/token_storage_service.dart';
|
import '../service/token_storage_service.dart';
|
||||||
import 'RaiseClaimForm.dart';
|
import 'RaiseClaimForm.dart';
|
||||||
@ -74,26 +75,14 @@ class _ClaimsPolicieState extends State<ClaimsPolicies> {
|
|||||||
int? appliedClaimStatus;
|
int? appliedClaimStatus;
|
||||||
|
|
||||||
Color getStatusColor(String status) {
|
Color getStatusColor(String status) {
|
||||||
switch (status.toLowerCase()) {
|
final normalized = status.toLowerCase().trim();
|
||||||
case 'claim received':
|
if (normalized == 'rejected' || normalized.contains('rejected')) {
|
||||||
return const Color(0xFF4A90E2); // blue
|
return const Color(0xFFE26728); // Export button orange
|
||||||
case 'under process':
|
|
||||||
return const Color(0xFFF5A623); // orange
|
|
||||||
case 'information required':
|
|
||||||
return const Color(0xFFFF8C42); // amber
|
|
||||||
case 'approved':
|
|
||||||
return const Color(0xFF2ECC71); // green
|
|
||||||
case 'settled':
|
|
||||||
return const Color(0xFF1ABC9C); // teal-green
|
|
||||||
case 'rejected':
|
|
||||||
return const Color(0xFFE74C3C); // red
|
|
||||||
case 'denial review awaite':
|
|
||||||
return const Color(0xFF9B59B6); // purple
|
|
||||||
case 'closed':
|
|
||||||
return const Color(0xFFB0BEC5); // purple
|
|
||||||
default:
|
|
||||||
return const Color(0xFFB0BEC5); // grey fallback
|
|
||||||
}
|
}
|
||||||
|
if (normalized == 'settled' || normalized.contains('settled')) {
|
||||||
|
return const Color(0xFF2ECC71); // Green
|
||||||
|
}
|
||||||
|
return const Color(0xFF00A6A6); // List header color
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<String, dynamic> claim_Detials() {
|
Map<String, dynamic> claim_Detials() {
|
||||||
@ -148,7 +137,22 @@ class _ClaimsPolicieState extends State<ClaimsPolicies> {
|
|||||||
// }
|
// }
|
||||||
|
|
||||||
// getApiData();
|
// getApiData();
|
||||||
_loadIds();
|
_checkToken();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _checkToken() async {
|
||||||
|
final token = await tokenService.getCurrentToken();
|
||||||
|
if (token == null || token.trim().isEmpty) {
|
||||||
|
if (!mounted) return;
|
||||||
|
ToastHelper.showErrorToast(context, 'Session Out');
|
||||||
|
Navigator.pushNamedAndRemoveUntil(
|
||||||
|
context,
|
||||||
|
'hrLogin',
|
||||||
|
(route) => false,
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await _loadIds();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _loadIds() async {
|
Future<void> _loadIds() async {
|
||||||
|
|||||||
@ -41,6 +41,7 @@ class _ClaimsOverviewDashboardState extends State<ClaimsOverviewDashboard>
|
|||||||
bool _isLoading = true;
|
bool _isLoading = true;
|
||||||
bool _isRefreshing = false;
|
bool _isRefreshing = false;
|
||||||
bool _isExportingPdf = false;
|
bool _isExportingPdf = false;
|
||||||
|
bool _sessionChecked = false;
|
||||||
int _replayToken = 0;
|
int _replayToken = 0;
|
||||||
int _currentTab = 0;
|
int _currentTab = 0;
|
||||||
|
|
||||||
@ -89,7 +90,25 @@ class _ClaimsOverviewDashboardState extends State<ClaimsOverviewDashboard>
|
|||||||
);
|
);
|
||||||
_headerFadeController.forward();
|
_headerFadeController.forward();
|
||||||
|
|
||||||
_loadDashboard();
|
_checkToken();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _checkToken() async {
|
||||||
|
final token = await _tokenService.getCurrentToken();
|
||||||
|
if (token == null || token.trim().isEmpty) {
|
||||||
|
if (!mounted) return;
|
||||||
|
ToastHelper.showErrorToast(context, 'Session Out');
|
||||||
|
Navigator.pushNamedAndRemoveUntil(
|
||||||
|
context,
|
||||||
|
'hrLogin',
|
||||||
|
(route) => false,
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!mounted) return;
|
||||||
|
setState(() => _sessionChecked = true);
|
||||||
|
await _loadDashboard();
|
||||||
}
|
}
|
||||||
|
|
||||||
void _onTabChanged() {
|
void _onTabChanged() {
|
||||||
@ -499,6 +518,16 @@ class _ClaimsOverviewDashboardState extends State<ClaimsOverviewDashboard>
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
if (!_sessionChecked) {
|
||||||
|
return const Scaffold(
|
||||||
|
body: Center(
|
||||||
|
child: CircularProgressIndicator(
|
||||||
|
color: Color(0xFF00999E),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return BaseLayout(
|
return BaseLayout(
|
||||||
child: SecurePopScope(
|
child: SecurePopScope(
|
||||||
child: Container(
|
child: Container(
|
||||||
|
|||||||
@ -62,6 +62,8 @@ class _ClaimHistoryPopupState extends State<ClaimHistoryPopup>
|
|||||||
bool isSubmitting = false;
|
bool isSubmitting = false;
|
||||||
|
|
||||||
bool isActionFreeze = false;
|
bool isActionFreeze = false;
|
||||||
|
bool useStatusKeyedTicketData = false;
|
||||||
|
bool useListTicketHistory = false;
|
||||||
|
|
||||||
// IR Docs state
|
// IR Docs state
|
||||||
bool showIRDocs = false;
|
bool showIRDocs = false;
|
||||||
@ -275,22 +277,28 @@ class _ClaimHistoryPopupState extends State<ClaimHistoryPopup>
|
|||||||
if (response['status'] == 'success') {
|
if (response['status'] == 'success') {
|
||||||
setState(() {
|
setState(() {
|
||||||
isLoading = false;
|
isLoading = false;
|
||||||
final claimsDocs = response['data']['claim_files'];
|
final data = response['data'] is Map
|
||||||
claimFiles = List<Map<String, dynamic>>.from(claimsDocs);
|
? Map<String, dynamic>.from(response['data'])
|
||||||
|
: <String, dynamic>{};
|
||||||
|
|
||||||
getClaimsHistoryList = [
|
final claimsDocs = data['claim_files'];
|
||||||
Map<String, dynamic>.from(response['data']['ticket_data'])
|
claimFiles = claimsDocs is List
|
||||||
];
|
? List<Map<String, dynamic>>.from(claimsDocs)
|
||||||
stepMap = getClaimsHistoryList[0];
|
: <Map<String, dynamic>>[];
|
||||||
stepKeys = stepMap.keys.toList();
|
|
||||||
|
|
||||||
final isFreeze =
|
_parseTicketData(data);
|
||||||
response['data']['required_docs']['is_action_freeze'] ?? false;
|
|
||||||
setState(() {
|
final requiredDocsRoot = data['required_docs'];
|
||||||
isActionFreeze = isFreeze;
|
if (requiredDocsRoot is Map) {
|
||||||
});
|
isActionFreeze = requiredDocsRoot['is_action_freeze'] ?? false;
|
||||||
final requiredDocs = response['data']['required_docs']['docs'];
|
final requiredDocs = requiredDocsRoot['docs'];
|
||||||
requiredDocsList = List<Map<String, dynamic>>.from(requiredDocs);
|
requiredDocsList = requiredDocs is List
|
||||||
|
? List<Map<String, dynamic>>.from(requiredDocs)
|
||||||
|
: <Map<String, dynamic>>[];
|
||||||
|
} else {
|
||||||
|
isActionFreeze = false;
|
||||||
|
requiredDocsList = <Map<String, dynamic>>[];
|
||||||
|
}
|
||||||
|
|
||||||
// ⭐ Make a backup copy to restore later
|
// ⭐ Make a backup copy to restore later
|
||||||
requiredDocsListBackup = requiredDocsList
|
requiredDocsListBackup = requiredDocsList
|
||||||
@ -317,6 +325,60 @@ class _ClaimHistoryPopupState extends State<ClaimHistoryPopup>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _parseTicketData(Map<String, dynamic> data) {
|
||||||
|
final ticketDataRaw = data['ticket_data'];
|
||||||
|
final ticketHistoryRaw = data['ticket_history'];
|
||||||
|
|
||||||
|
useStatusKeyedTicketData = false;
|
||||||
|
useListTicketHistory = false;
|
||||||
|
stepMap = <String, dynamic>{};
|
||||||
|
stepKeys = [];
|
||||||
|
getClaimsHistoryList = [];
|
||||||
|
|
||||||
|
if (ticketDataRaw is Map &&
|
||||||
|
!ticketDataRaw.containsKey('status') &&
|
||||||
|
ticketDataRaw.values.any((value) => value is Map)) {
|
||||||
|
useStatusKeyedTicketData = true;
|
||||||
|
stepMap = Map<String, dynamic>.from(ticketDataRaw);
|
||||||
|
stepKeys = stepMap.keys.map((key) => key.toString()).toList();
|
||||||
|
getClaimsHistoryList = [stepMap];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ticketDataRaw is List && ticketDataRaw.isNotEmpty) {
|
||||||
|
useListTicketHistory = true;
|
||||||
|
getClaimsHistoryList = ticketDataRaw
|
||||||
|
.whereType<Map>()
|
||||||
|
.map((item) => Map<String, dynamic>.from(item))
|
||||||
|
.toList();
|
||||||
|
stepKeys = List.generate(getClaimsHistoryList.length, (index) => '$index');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ticketHistoryRaw is List && ticketHistoryRaw.isNotEmpty) {
|
||||||
|
useListTicketHistory = true;
|
||||||
|
getClaimsHistoryList = ticketHistoryRaw
|
||||||
|
.whereType<Map>()
|
||||||
|
.map((item) => Map<String, dynamic>.from(item))
|
||||||
|
.toList();
|
||||||
|
stepKeys = List.generate(getClaimsHistoryList.length, (index) => '$index');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ticketDataRaw is Map && ticketDataRaw.isNotEmpty) {
|
||||||
|
useListTicketHistory = true;
|
||||||
|
getClaimsHistoryList = [Map<String, dynamic>.from(ticketDataRaw)];
|
||||||
|
stepKeys = const ['0'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> _asStringMap(dynamic value) {
|
||||||
|
if (value is Map) {
|
||||||
|
return Map<String, dynamic>.from(value);
|
||||||
|
}
|
||||||
|
return <String, dynamic>{};
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> _launchURL(String url) async {
|
Future<void> _launchURL(String url) async {
|
||||||
final Uri uri = Uri.parse(url);
|
final Uri uri = Uri.parse(url);
|
||||||
try {
|
try {
|
||||||
@ -660,13 +722,23 @@ class _ClaimHistoryPopupState extends State<ClaimHistoryPopup>
|
|||||||
? Column(
|
? Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: List.generate(stepKeys.length, (index) {
|
children: List.generate(stepKeys.length, (index) {
|
||||||
String stepTitleKey = stepKeys[index];
|
if (useListTicketHistory) {
|
||||||
Map<String, dynamic> stepData = stepMap[stepTitleKey];
|
final stepData = getClaimsHistoryList[index];
|
||||||
Widget content = _getStepContentFromApi(stepData);
|
return _buildStep(
|
||||||
|
stepNumber: index + 1,
|
||||||
|
title: _getStepTitleFromHistoryItem(stepData),
|
||||||
|
content: _getStepContentFromHistoryItem(stepData),
|
||||||
|
isLast: index == stepKeys.length - 1,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
final stepTitleKey = stepKeys[index];
|
||||||
|
final stepData = _asStringMap(stepMap[stepTitleKey]);
|
||||||
return _buildStep(
|
return _buildStep(
|
||||||
stepNumber: index + 1,
|
stepNumber: index + 1,
|
||||||
title: _getStepTitleFromApi(stepTitleKey, stepData),
|
title: _getStepTitleFromApi(
|
||||||
content: content,
|
stepTitleKey, stepData),
|
||||||
|
content: _getStepContentFromApi(stepData),
|
||||||
isLast: index == stepKeys.length - 1,
|
isLast: index == stepKeys.length - 1,
|
||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
@ -1459,6 +1531,53 @@ class _ClaimHistoryPopupState extends State<ClaimHistoryPopup>
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget _getStepTitleFromHistoryItem(Map<String, dynamic> data) {
|
||||||
|
final title =
|
||||||
|
(data['display_name'] ?? data['field_name'] ?? 'Update').toString();
|
||||||
|
final modifiedBy = (data['modified_by'] ?? '').toString();
|
||||||
|
final modifiedAt =
|
||||||
|
(data['created_at'] ?? data['modified_at'] ?? '').toString();
|
||||||
|
final symbol = modifiedBy.isNotEmpty ? ' - ' : '';
|
||||||
|
final isDesktop = Responsive.isDesktop(context);
|
||||||
|
|
||||||
|
return RichText(
|
||||||
|
text: TextSpan(
|
||||||
|
children: [
|
||||||
|
TextSpan(
|
||||||
|
text: title + (isDesktop ? ' ' : '\n'),
|
||||||
|
style: GoogleFonts.poppins(
|
||||||
|
fontSize: Responsive.isDesktop(context) ? 15 : 11,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
color: Color(0xFF212120)),
|
||||||
|
),
|
||||||
|
TextSpan(
|
||||||
|
text: modifiedAt.isEmpty ? '' : ' ($modifiedBy$symbol$modifiedAt)',
|
||||||
|
style: GoogleFonts.poppins(
|
||||||
|
fontSize: Responsive.isDesktop(context) ? 14 : 11,
|
||||||
|
fontWeight: FontWeight.w400,
|
||||||
|
color: Color(0xFF565656)),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _getStepContentFromHistoryItem(Map<String, dynamic> data) {
|
||||||
|
final oldValue =
|
||||||
|
(data['old_status_value'] ?? data['old_value'] ?? '-').toString();
|
||||||
|
final newValue =
|
||||||
|
(data['new_status_value'] ?? data['new_value'] ?? '-').toString();
|
||||||
|
|
||||||
|
return Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
_buildHistoryListData('Previous Value', oldValue),
|
||||||
|
const SizedBox(height: 1),
|
||||||
|
_buildHistoryListData('Updated Value', newValue),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
Widget _getStepTitleFromApi(String status, Map<String, dynamic> data) {
|
Widget _getStepTitleFromApi(String status, Map<String, dynamic> data) {
|
||||||
final modifiedBy = data['modified_by'] ?? '';
|
final modifiedBy = data['modified_by'] ?? '';
|
||||||
final modifiedAt = data['modified_at'] ?? '';
|
final modifiedAt = data['modified_at'] ?? '';
|
||||||
@ -1489,14 +1608,36 @@ class _ClaimHistoryPopupState extends State<ClaimHistoryPopup>
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget _getStepContentFromApi(Map<String, dynamic> data) {
|
Widget _getStepContentFromApi(Map<String, dynamic> data) {
|
||||||
List<Widget> rows = [];
|
final rows = <Widget>[];
|
||||||
data.forEach((key, value) {
|
|
||||||
if (key == 'modified_by' || key == 'modified_at') return;
|
for (final entry in data.entries) {
|
||||||
String displayName = value['display_name'] ?? key;
|
final key = entry.key.toString();
|
||||||
String displayValue = value['display_value'] ?? 'N/A';
|
final value = entry.value;
|
||||||
rows.add(_buildHistoryListData(displayName, displayValue));
|
|
||||||
rows.add(SizedBox(height: 1));
|
if (key == 'modified_by' || key == 'modified_at') continue;
|
||||||
});
|
|
||||||
|
if (value is Map) {
|
||||||
|
final nested = Map<String, dynamic>.from(value);
|
||||||
|
final displayName =
|
||||||
|
(nested['display_name'] ?? key).toString();
|
||||||
|
final displayValue =
|
||||||
|
(nested['display_value'] ?? 'N/A').toString();
|
||||||
|
rows
|
||||||
|
..add(_buildHistoryListData(displayName, displayValue))
|
||||||
|
..add(const SizedBox(height: 1));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (key == 'reason') {
|
||||||
|
final reason = value?.toString().trim() ?? '';
|
||||||
|
if (reason.isNotEmpty) {
|
||||||
|
rows
|
||||||
|
..add(_buildReasonText(reason))
|
||||||
|
..add(const SizedBox(height: 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return Column(crossAxisAlignment: CrossAxisAlignment.start, children: rows);
|
return Column(crossAxisAlignment: CrossAxisAlignment.start, children: rows);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1550,6 +1691,24 @@ class _ClaimHistoryPopupState extends State<ClaimHistoryPopup>
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget _buildReasonText(String value) {
|
||||||
|
final displayValue =
|
||||||
|
(value.trim().isEmpty) ? 'N/A' : value.trim();
|
||||||
|
|
||||||
|
return Align(
|
||||||
|
alignment: Alignment.centerLeft,
|
||||||
|
child: Text(
|
||||||
|
displayValue,
|
||||||
|
textAlign: TextAlign.left,
|
||||||
|
style: GoogleFonts.poppins(
|
||||||
|
color: const Color(0xFF000000),
|
||||||
|
fontWeight: FontWeight.w400,
|
||||||
|
fontSize: Responsive.isDesktop(context) ? 14 : 12,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
Widget _buildHistoryListData(String title, String value) {
|
Widget _buildHistoryListData(String title, String value) {
|
||||||
final displayValue =
|
final displayValue =
|
||||||
(value == null || value.trim().isEmpty) ? 'N/A' : value;
|
(value == null || value.trim().isEmpty) ? 'N/A' : value;
|
||||||
|
|||||||
@ -58,6 +58,7 @@ class _NonEBClaimsHistoryState extends State<NonEBClaimsHistory>
|
|||||||
bool isSubmitting = false;
|
bool isSubmitting = false;
|
||||||
|
|
||||||
bool isActionFreeze = false;
|
bool isActionFreeze = false;
|
||||||
|
bool useStatusKeyedTicketData = false;
|
||||||
|
|
||||||
// IR Docs state
|
// IR Docs state
|
||||||
bool showIRDocs = false;
|
bool showIRDocs = false;
|
||||||
@ -286,20 +287,34 @@ class _NonEBClaimsHistoryState extends State<NonEBClaimsHistory>
|
|||||||
|
|
||||||
final ticketDataRaw = root['ticket_data'] ?? nestedData['ticket_data'];
|
final ticketDataRaw = root['ticket_data'] ?? nestedData['ticket_data'];
|
||||||
if (ticketDataRaw is List) {
|
if (ticketDataRaw is List) {
|
||||||
|
useStatusKeyedTicketData = false;
|
||||||
getClaimsHistoryList = ticketDataRaw
|
getClaimsHistoryList = ticketDataRaw
|
||||||
.map<Map<String, dynamic>>((e) => Map<String, dynamic>.from(e))
|
.map<Map<String, dynamic>>((e) => Map<String, dynamic>.from(e))
|
||||||
.toList();
|
.toList();
|
||||||
|
stepMap = getClaimsHistoryList.isNotEmpty
|
||||||
|
? getClaimsHistoryList.first
|
||||||
|
: <String, dynamic>{};
|
||||||
|
stepKeys = List.generate(getClaimsHistoryList.length, (i) => '$i');
|
||||||
} else if (ticketDataRaw is Map) {
|
} else if (ticketDataRaw is Map) {
|
||||||
getClaimsHistoryList = [Map<String, dynamic>.from(ticketDataRaw)];
|
final map = Map<String, dynamic>.from(ticketDataRaw);
|
||||||
|
if (map.containsKey('status')) {
|
||||||
|
useStatusKeyedTicketData = false;
|
||||||
|
getClaimsHistoryList = [map];
|
||||||
|
stepMap = map;
|
||||||
|
stepKeys = const ['0'];
|
||||||
|
} else {
|
||||||
|
useStatusKeyedTicketData = true;
|
||||||
|
stepMap = map;
|
||||||
|
stepKeys = map.keys.map((k) => k.toString()).toList();
|
||||||
|
getClaimsHistoryList = [map];
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
|
useStatusKeyedTicketData = false;
|
||||||
getClaimsHistoryList = [];
|
getClaimsHistoryList = [];
|
||||||
|
stepMap = <String, dynamic>{};
|
||||||
|
stepKeys = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
stepMap = getClaimsHistoryList.isNotEmpty
|
|
||||||
? getClaimsHistoryList.first
|
|
||||||
: <String, dynamic>{};
|
|
||||||
stepKeys = List.generate(getClaimsHistoryList.length, (i) => '$i');
|
|
||||||
|
|
||||||
final claimsDocs = root['claims_files'] ??
|
final claimsDocs = root['claims_files'] ??
|
||||||
root['claim_files'] ??
|
root['claim_files'] ??
|
||||||
nestedData['claims_files'] ??
|
nestedData['claims_files'] ??
|
||||||
@ -688,13 +703,25 @@ class _NonEBClaimsHistoryState extends State<NonEBClaimsHistory>
|
|||||||
child: getClaimsHistoryList.isNotEmpty
|
child: getClaimsHistoryList.isNotEmpty
|
||||||
? Column(
|
? Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: List.generate(getClaimsHistoryList.length, (index) {
|
children: useStatusKeyedTicketData
|
||||||
|
? List.generate(stepKeys.length, (index) {
|
||||||
|
final stepTitleKey = stepKeys[index];
|
||||||
|
final stepData = Map<String, dynamic>.from(
|
||||||
|
stepMap[stepTitleKey] ?? <String, dynamic>{});
|
||||||
|
return _buildStep(
|
||||||
|
stepNumber: index + 1,
|
||||||
|
title: _getStepTitleFromStatusKey(
|
||||||
|
stepTitleKey, stepData),
|
||||||
|
content: _getStepContentFromApi(stepData),
|
||||||
|
isLast: index == stepKeys.length - 1,
|
||||||
|
);
|
||||||
|
})
|
||||||
|
: List.generate(getClaimsHistoryList.length, (index) {
|
||||||
final stepData = getClaimsHistoryList[index];
|
final stepData = getClaimsHistoryList[index];
|
||||||
Widget content = _getStepContentFromApi(stepData);
|
|
||||||
return _buildStep(
|
return _buildStep(
|
||||||
stepNumber: index + 1,
|
stepNumber: index + 1,
|
||||||
title: _getStepTitleFromApi(stepData),
|
title: _getStepTitleFromApi(stepData),
|
||||||
content: content,
|
content: _getStepContentFromApi(stepData),
|
||||||
isLast: index == getClaimsHistoryList.length - 1,
|
isLast: index == getClaimsHistoryList.length - 1,
|
||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
@ -1487,6 +1514,35 @@ class _NonEBClaimsHistoryState extends State<NonEBClaimsHistory>
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget _getStepTitleFromStatusKey(String status, Map<String, dynamic> data) {
|
||||||
|
final modifiedBy = (data['modified_by'] ?? '').toString();
|
||||||
|
final modifiedAt =
|
||||||
|
(data['modified_at'] ?? data['changed_at'] ?? '').toString();
|
||||||
|
final symbol = modifiedBy.isNotEmpty ? ' - ' : '';
|
||||||
|
final isDesktop = Responsive.isDesktop(context);
|
||||||
|
|
||||||
|
return RichText(
|
||||||
|
text: TextSpan(
|
||||||
|
children: [
|
||||||
|
TextSpan(
|
||||||
|
text: status + (isDesktop ? ' ' : '\n'),
|
||||||
|
style: GoogleFonts.poppins(
|
||||||
|
fontSize: Responsive.isDesktop(context) ? 15 : 11,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
color: Color(0xFF212120)),
|
||||||
|
),
|
||||||
|
TextSpan(
|
||||||
|
text: modifiedAt.isEmpty ? '' : ' ($modifiedBy$symbol$modifiedAt)',
|
||||||
|
style: GoogleFonts.poppins(
|
||||||
|
fontSize: Responsive.isDesktop(context) ? 14 : 11,
|
||||||
|
fontWeight: FontWeight.w400,
|
||||||
|
color: Color(0xFF565656)),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
Widget _getStepTitleFromApi(Map<String, dynamic> data) {
|
Widget _getStepTitleFromApi(Map<String, dynamic> data) {
|
||||||
final status = (data['status'] ?? '').toString();
|
final status = (data['status'] ?? '').toString();
|
||||||
final changedAt =
|
final changedAt =
|
||||||
@ -1526,11 +1582,22 @@ class _NonEBClaimsHistoryState extends State<NonEBClaimsHistory>
|
|||||||
key == 'changed_at') {
|
key == 'changed_at') {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (value is! Map) return;
|
|
||||||
String displayName = value['display_name'] ?? key;
|
if (value is Map) {
|
||||||
String displayValue = value['display_value'] ?? 'N/A';
|
final displayName = value['display_name']?.toString() ?? key;
|
||||||
rows.add(_buildHistoryListData(displayName, displayValue));
|
final displayValue = value['display_value']?.toString() ?? 'N/A';
|
||||||
rows.add(SizedBox(height: 1));
|
rows.add(_buildHistoryListData(displayName, displayValue));
|
||||||
|
rows.add(const SizedBox(height: 1));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (key == 'reason' && value != null) {
|
||||||
|
final reason = value.toString().trim();
|
||||||
|
if (reason.isNotEmpty) {
|
||||||
|
rows.add(_buildReasonText(reason));
|
||||||
|
rows.add(const SizedBox(height: 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
return Column(crossAxisAlignment: CrossAxisAlignment.start, children: rows);
|
return Column(crossAxisAlignment: CrossAxisAlignment.start, children: rows);
|
||||||
}
|
}
|
||||||
@ -1585,6 +1652,24 @@ class _NonEBClaimsHistoryState extends State<NonEBClaimsHistory>
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget _buildReasonText(String value) {
|
||||||
|
final displayValue =
|
||||||
|
(value.trim().isEmpty) ? 'N/A' : value.trim();
|
||||||
|
|
||||||
|
return Align(
|
||||||
|
alignment: Alignment.centerLeft,
|
||||||
|
child: Text(
|
||||||
|
displayValue,
|
||||||
|
textAlign: TextAlign.left,
|
||||||
|
style: GoogleFonts.poppins(
|
||||||
|
color: const Color(0xFF000000),
|
||||||
|
fontWeight: FontWeight.w400,
|
||||||
|
fontSize: Responsive.isDesktop(context) ? 14 : 12,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
Widget _buildHistoryListData(String title, String value) {
|
Widget _buildHistoryListData(String title, String value) {
|
||||||
final displayValue =
|
final displayValue =
|
||||||
(value == null || value.trim().isEmpty) ? 'N/A' : value;
|
(value == null || value.trim().isEmpty) ? 'N/A' : value;
|
||||||
|
|||||||
@ -16,6 +16,7 @@ import 'package:universal_html/html.dart' as html;
|
|||||||
import '../service/secure_pop_scope.dart';
|
import '../service/secure_pop_scope.dart';
|
||||||
import 'claimshistory.dart';
|
import 'claimshistory.dart';
|
||||||
import '../customAppBar/base_layout.dart';
|
import '../customAppBar/base_layout.dart';
|
||||||
|
import '../customAppBar/toastHelper.dart';
|
||||||
import '../service/api_service.dart';
|
import '../service/api_service.dart';
|
||||||
import '../service/token_storage_service.dart';
|
import '../service/token_storage_service.dart';
|
||||||
import 'RaiseClaimForm.dart';
|
import 'RaiseClaimForm.dart';
|
||||||
@ -81,34 +82,18 @@ class _NonEBClaimsListState extends State<NonEBClaimsList> {
|
|||||||
int? appliedClaimStatus;
|
int? appliedClaimStatus;
|
||||||
|
|
||||||
Color getStatusColor(String status) {
|
Color getStatusColor(String status) {
|
||||||
switch (status.toLowerCase().trim()) {
|
final normalized = status.toLowerCase().trim();
|
||||||
case 'claim intimation received':
|
if (normalized == 'rejected' ||
|
||||||
return const Color(0xFF4A90E2);
|
normalized == 'claim rejected' ||
|
||||||
case 'claim intimated':
|
normalized.contains('rejected')) {
|
||||||
return const Color(0xFF5B9BD5);
|
return const Color(0xFFE26728); // Export button orange
|
||||||
case 'insurer pending':
|
|
||||||
return const Color(0xFFF5A623);
|
|
||||||
case 'surveyor pending':
|
|
||||||
return const Color(0xFFFF8C42);
|
|
||||||
case 'insured pending':
|
|
||||||
return const Color(0xFFE67E22);
|
|
||||||
case 'insurer / surveyor pending':
|
|
||||||
return const Color(0xFFD35400);
|
|
||||||
case 'workshop pending':
|
|
||||||
return const Color(0xFF9B59B6);
|
|
||||||
case 'claim approved':
|
|
||||||
return const Color(0xFF2ECC71);
|
|
||||||
case 'claim settled':
|
|
||||||
return const Color(0xFF1ABC9C);
|
|
||||||
case 'claim closed':
|
|
||||||
return const Color(0xFF95A5A6);
|
|
||||||
case 'claim rejected':
|
|
||||||
return const Color(0xFFE74C3C);
|
|
||||||
case 'claim withdrawn':
|
|
||||||
return const Color(0xFF7F8C8D);
|
|
||||||
default:
|
|
||||||
return const Color(0xFFB0BEC5);
|
|
||||||
}
|
}
|
||||||
|
if (normalized == 'settled' ||
|
||||||
|
normalized == 'claim settled' ||
|
||||||
|
normalized.contains('settled')) {
|
||||||
|
return const Color(0xFF2ECC71); // Green
|
||||||
|
}
|
||||||
|
return const Color(0xFF00A6A6); // List header color
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<String, dynamic> claim_Detials() {
|
Map<String, dynamic> claim_Detials() {
|
||||||
@ -169,7 +154,22 @@ class _NonEBClaimsListState extends State<NonEBClaimsList> {
|
|||||||
// }
|
// }
|
||||||
|
|
||||||
// getApiData();
|
// getApiData();
|
||||||
_loadIds();
|
_checkToken();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _checkToken() async {
|
||||||
|
final token = await tokenService.getCurrentToken();
|
||||||
|
if (token == null || token.trim().isEmpty) {
|
||||||
|
if (!mounted) return;
|
||||||
|
ToastHelper.showErrorToast(context, 'Session Out');
|
||||||
|
Navigator.pushNamedAndRemoveUntil(
|
||||||
|
context,
|
||||||
|
'hrLogin',
|
||||||
|
(route) => false,
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await _loadIds();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _loadIds() async {
|
Future<void> _loadIds() async {
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
import 'dart:typed_data';
|
import 'dart:typed_data';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
|
||||||
import 'package:google_fonts/google_fonts.dart';
|
import 'package:google_fonts/google_fonts.dart';
|
||||||
import 'package:jwt_decode/jwt_decode.dart';
|
import 'package:jwt_decode/jwt_decode.dart';
|
||||||
import 'package:nhancepolicy/customAppBar/customAppBar.dart';
|
import 'package:nhancepolicy/customAppBar/customAppBar.dart';
|
||||||
|
|||||||
@ -137,6 +137,15 @@ class TokenStorageService {
|
|||||||
key: _branchNameKey,
|
key: _branchNameKey,
|
||||||
value: branch['branch_name']?.toString() ?? '',
|
value: branch['branch_name']?.toString() ?? '',
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Prefer claims_sub_menu from branch payload when available.
|
||||||
|
final claimsSubMenu = branch['claims_sub_menu'];
|
||||||
|
if (claimsSubMenu is List) {
|
||||||
|
await _secureStorage.write(
|
||||||
|
key: 'claims_sub_menu',
|
||||||
|
value: jsonEncode(claimsSubMenu),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 🔓 Decode JWT
|
// 🔓 Decode JWT
|
||||||
@ -225,6 +234,15 @@ class TokenStorageService {
|
|||||||
|
|
||||||
// ================= TOKEN =================
|
// ================= TOKEN =================
|
||||||
await _secureStorage.write(key: 'token', value: token);
|
await _secureStorage.write(key: 'token', value: token);
|
||||||
|
|
||||||
|
// ================= CLAIMS SUB MENU =================
|
||||||
|
if (decodedToken.containsKey('claims_sub_menu')) {
|
||||||
|
final claimsSubMenu = decodedToken['claims_sub_menu'];
|
||||||
|
await _secureStorage.write(
|
||||||
|
key: 'claims_sub_menu',
|
||||||
|
value: jsonEncode(claimsSubMenu is List ? claimsSubMenu : []),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<String?> readValue(String key) async {
|
Future<String?> readValue(String key) async {
|
||||||
@ -257,6 +275,7 @@ class TokenStorageService {
|
|||||||
'enrollmentEmpPrimaryId',
|
'enrollmentEmpPrimaryId',
|
||||||
'enrollmentHrId',
|
'enrollmentHrId',
|
||||||
'token',
|
'token',
|
||||||
|
'claims_sub_menu',
|
||||||
];
|
];
|
||||||
|
|
||||||
for (final key in keysToRemove) {
|
for (final key in keysToRemove) {
|
||||||
|
|||||||
@ -53,7 +53,7 @@ dependencies:
|
|||||||
flutter_animated_button: ^2.0.3
|
flutter_animated_button: ^2.0.3
|
||||||
spreadsheet_decoder: ^2.2.0
|
spreadsheet_decoder: ^2.2.0
|
||||||
url_launcher: ^6.2.6
|
url_launcher: ^6.2.6
|
||||||
font_awesome_flutter: ^10.7.0
|
font_awesome_flutter: ^11.0.0
|
||||||
archive: ^3.4.9
|
archive: ^3.4.9
|
||||||
dropdown_search: ^6.0.2
|
dropdown_search: ^6.0.2
|
||||||
flutter_secure_storage: ^10.0.0
|
flutter_secure_storage: ^10.0.0
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user