branch module

This commit is contained in:
Surendiran 2025-10-11 18:04:18 +05:30
parent f5cd4d89ad
commit 01f99fa2ca
13 changed files with 880 additions and 243 deletions

View File

@ -1,4 +1,4 @@
API_URL=https://venbait.in/enrollment/employeeRest/
API_URL_POST=https://venbait.in/nhance/dev/employeeRest/
BASE_HREF=/nhance/hr/dev/
ENV=development
API_URL=https://app.nhanceindia.in/enrolment/employeeRest/
API_URL_POST=https://app.nhanceindia.in/zenith/employeeRest/
BASE_HREF=/hr/
ENV=production

View File

@ -45,7 +45,7 @@ android {
applicationId "com.example.nhance_app_pwa"
// You can update the following values to match your application needs.
// For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration.
minSdkVersion 23
minSdkVersion flutter.minSdkVersion
targetSdkVersion flutter.targetSdkVersion
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName

View File

@ -0,0 +1,63 @@
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
class BranchCard extends StatelessWidget {
final String clientName;
final String branchName;
final bool isSelected;
final VoidCallback onTap;
const BranchCard({
Key? key,
required this.clientName,
required this.branchName,
required this.isSelected,
required this.onTap,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return InkWell(
onTap: onTap,
borderRadius: BorderRadius.circular(8),
child: Container(
decoration: BoxDecoration(
color: isSelected ? Color(0xFF00999E) : Color(0xFFF0F9F9),
borderRadius: BorderRadius.circular(8),
border: Border.all(
color: Color(0xFF00999E),
width: 1.5,
),
),
padding: EdgeInsets.all(12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
clientName,
style: GoogleFonts.poppins(
fontSize: 16,
fontWeight: FontWeight.w500,
color: isSelected ? Colors.white : Color(0xFF00999E),
),
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
SizedBox(height: 4),
Text(
branchName,
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w400,
color: isSelected ? Color(0xFFFDFDFD) : Color(0xFF000000),
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
],
),
),
);
}
}

View File

@ -0,0 +1,272 @@
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
import '../customAppBar/customAppBar.dart';
import '../customAppBar/customFooter.dart';
import '../customAppBar/toastHelper.dart';
import '../service/token_storage_service.dart';
import 'branch_card_widget.dart';
class BranchSelectionPage extends StatefulWidget {
const BranchSelectionPage({Key? key}) : super(key: key);
@override
State<BranchSelectionPage> createState() => _BranchSelectionPageState();
}
class _BranchSelectionPageState extends State<BranchSelectionPage> {
List<Map<String, dynamic>> branches = [];
int? selectedIndex;
final tokenStorage = TokenStorageService();
@override
void initState() {
super.initState();
_loadBranches();
}
void _loadBranches() {
setState(() {
branches = tokenStorage.getCombinedBranches();
});
}
void _selectBranch(int index) {
setState(() {
selectedIndex = index;
});
}
Future<void> _handleNext() async {
if (selectedIndex == null) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Please select a branch'),
backgroundColor: Colors.orange,
),
);
return;
}
final selectedBranch = branches[selectedIndex!];
// Save selected branch and decode token
await tokenStorage.saveSelectedBranch(selectedBranch);
// Debug: Print decoded token
final decodedToken = tokenStorage.getDecodedToken();
print('Selected Branch: $selectedBranch');
print('Decoded Token: $decodedToken');
// Navigate to home
if (mounted) {
final SharedPreferences prefs = await SharedPreferences.getInstance();
//Post Token Decode Details
final empClientBranchId = decodedToken?['post_branch_id'];
await prefs.setString('empClientBranchId', empClientBranchId);
final empPrimaryId = decodedToken?['post_hr_id'];
await prefs.setString('empPrimaryId', empPrimaryId);
final empClientId = decodedToken?['post_client_id'];
await prefs.setString('empClientId', empClientId);
final empHrId = decodedToken?['post_hr_id'];
await prefs.setString('empHrId', empHrId);
dynamic allowedModules = decodedToken?['allowed_modules'];
// If it's a String, decode it again
if (allowedModules is String) {
allowedModules = jsonDecode(allowedModules);
}
final empAllowedModules = allowedModules['post'];
await prefs.setString('empAllowed_modules', jsonEncode(empAllowedModules));
// final empAllowed_modules = decodedToken?['allowed_modules'][0]['post'];
// await prefs.setString('empAllowed_modules', jsonEncode(empAllowed_modules));
//Pre Token Decode Details
final enrollmentEmpClientBranchId = decodedToken?['pre_branch_id'];
await prefs.setString('enrollmentEmpClientBranchId', enrollmentEmpClientBranchId);
final enrollmentEmpPrimaryId = decodedToken?['pre_hr_id'];
await prefs.setString('enrollmentEmpPrimaryId', enrollmentEmpPrimaryId);
final enrollmentClient_id = decodedToken?['pre_client_id'];
await prefs.setString('enrollmentClient_id', enrollmentClient_id);
final enrollmentHrId = decodedToken?['pre_hr_id'];
await prefs.setString('enrollmentHrId', enrollmentHrId);
final enrollmentAllowedModules = allowedModules['pre'];
await prefs.setString('enrollmentAllowed_modules', jsonEncode(enrollmentAllowedModules));
// final enrollmentAllowed_modules = decodedToken?['allowed_modules'][1]['pre'];
// await prefs.setString('enrollmentAllowed_modules', jsonEncode(enrollmentAllowed_modules));
final token = await tokenStorage.getCurrentToken();
print('tokenbranch - $token');
await prefs.setString('token', token!);
ToastHelper.showSuccessToast(context, 'Successfully Login');
Navigator.pushReplacementNamed(context, 'hrDashboard');
}
}
@override
Widget build(BuildContext context) {
final screenHeight = MediaQuery.of(context).size.height;
final screenWidth = MediaQuery.of(context).size.width;
// Calculate dynamic horizontal padding (10% of screen width)
final horizontalPadding = screenWidth * 0.1;
// Calculate dynamic grid height
double gridHeight = screenHeight * 0.45;
if (gridHeight < 300) gridHeight = 300;
if (gridHeight > 600) gridHeight = 600;
// Determine crossAxisCount with multiple breakpoints
int crossAxisCount;
double childAspectRatio;
if (screenWidth < 600) {
// Mobile phones
crossAxisCount = 2;
childAspectRatio = 2.5;
} else if (screenWidth < 900) {
// Small tablets
crossAxisCount = 2;
childAspectRatio = 2.8;
} else if (screenWidth < 1200) {
// Large tablets
crossAxisCount = 3;
childAspectRatio = 3;
} else {
// Desktop
crossAxisCount = 3;
childAspectRatio = 5;
}
return Scaffold(
backgroundColor: Color(0xFFEFF3F6),
appBar: CustomAppBar(),
body: Column(
children: [
Expanded(
child: SingleChildScrollView(
child: Padding(
padding: EdgeInsets.symmetric(
horizontal: horizontalPadding , // Inner horizontal padding
vertical: 20, // Inner vertical padding
), // Outer padding
child: Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.05),
blurRadius: 10,
offset: Offset(0, 4),
),
],
),
padding: const EdgeInsets.symmetric(
horizontal: 24.0, // Inner horizontal padding
vertical: 28.0, // Inner vertical padding
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Select Client',
style: TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
color: Colors.black,
),
),
SizedBox(height: 25),
Container(
height: gridHeight, // Shows approximately 3 rows
child: branches.isEmpty
? Center(
child: Text(
'No branches available',
style: TextStyle(fontSize: 16, color: Colors.grey),
),
)
: GridView.builder(
// Enable scrolling if content exceeds height
physics: ClampingScrollPhysics(),
padding: EdgeInsets.zero,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: crossAxisCount,
childAspectRatio: childAspectRatio,
crossAxisSpacing: 15,
mainAxisSpacing: 15,
),
itemCount: branches.length,
itemBuilder: (context, index) {
final branch = branches[index];
return BranchCard(
clientName: branch['client_name'] ?? 'Unknown Client',
branchName: branch['branch_name'] ?? 'Unknown Branch',
isSelected: selectedIndex == index,
onTap: () => _selectBranch(index),
);
},
),
),
SizedBox(height: 24),
Center(
child: SizedBox(
width: 120,
height: 48,
child: ElevatedButton(
onPressed: _handleNext,
style: ElevatedButton.styleFrom(
backgroundColor: Color(0xFFFF6B35),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
elevation: 0,
),
child: Text(
'Next',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: Colors.white,
),
),
),
),
),
],
),
),
),
),
),
Container(
width: double.infinity,
child: CustomFooter(),
),
],
),
);
}
}

View File

@ -87,12 +87,29 @@ class _CustomAppBarState extends State<CustomAppBar> {
// },
// ),
// if (showBackToHR)
// NavBarItem(
// text: "Back To HR",
// onTap: () {
// // hrLogout(context);
// },
// ),
NavBarItem(
text: "Change Branch",
onTap: () async {
final prefs = await SharedPreferences.getInstance();
await prefs.remove('selected_branch');
await prefs.remove('decoded_token');
await prefs.remove('clientLogo');
await prefs.remove('clientName');
await prefs.remove('empAllowed_modules');
await prefs.remove('empClientBranchId');
await prefs.remove('empClientId');
await prefs.remove('empEmail');
await prefs.remove('empHrId');
await prefs.remove('empPrimaryId');
await prefs.remove('enrollmentAllowed_modules');
await prefs.remove('enrollmentClient_id');
await prefs.remove('enrollmentEmpClientBranchId');
await prefs.remove('enrollmentEmpPrimaryId');
await prefs.remove('enrollmentHrId');
await prefs.remove('token');
Navigator.pushNamed(context, 'branchSelection');
},
),
NavBarItem(
text: "Logout",
onTap: () async {

View File

@ -4,6 +4,7 @@ import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:nhancepolicy/responsive.dart';
import 'package:nhancepolicy/service/api_service.dart';
import 'package:nhancepolicy/service/token_storage_service.dart';
import 'package:pinput/pinput.dart';
import 'dart:async';
import 'package:flutter/gestures.dart';
@ -17,16 +18,15 @@ import '../customAppBar/toastHelper.dart';
import '../models/environment.dart';
import '../models/platform_helper_mobile.dart'
if (dart.library.html) '../models/platform_helper_other.dart';
import 'branch/branch_selection_page.dart';
// import 'dart:html' as html;
class MyEmailVerify extends StatefulWidget {
final String email;
final String type; // 'email' or 'mobile'
final String value;
const MyEmailVerify({
Key? key,
required this.email,
}) : super(key: key);
const MyEmailVerify({super.key, required this.type, required this.value});
@override
State<MyEmailVerify> createState() => _MyEmailVerifyState();
@ -99,9 +99,12 @@ class _MyEmailVerifyState extends State<MyEmailVerify> {
void verifyOTP(String otp) async {
try {
final Map<String, dynamic> payload = (widget.type == 'mobile')
? {'otp': _otpController.text,'mobile_no': widget.value}
: {'otp': _otpController.text, 'email': widget.value};
final response = await http.post(
Uri.parse(Environment.apiUrl + 'getVerifiedHrData'),
body: json.encode({'email': widget.email, 'otp': _otpController.text}),
body: json.encode(payload),
headers: {
HttpHeaders.contentTypeHeader: 'application/json',
},
@ -112,35 +115,56 @@ class _MyEmailVerifyState extends State<MyEmailVerify> {
_isLoading = false;
});
Map<String, dynamic> data = json.decode(response.body);
print('data: $data');
_token = data['data'];
String status = data['status'];
print('Response data: $data');
// Directly access the post_enrollment data
Map<String, dynamic> post = data['post_enrollment'];
print('post: $post');
// Extract enrollment data
List<dynamic> preEnrollmentData = data['data'] ?? [];
Map<String, dynamic> postEnrollment = data['post_enrollment'] ?? {};
List<dynamic> postEnrollmentData = postEnrollment['data'] ?? [];
_postToken = post['data'];
String postStatus = post['status'];
// Save to global storage
final tokenStorage = TokenStorageService();
await tokenStorage.saveEnrollmentData(
preEnrollmentData,
postEnrollmentData,
);
if (postStatus == 'success') {
setState(() {
_isLoading = false;
});
postSuccessData(post, data);
} else if (status == 'success') {
setState(() {
_isLoading = false;
});
enrollmentSuccessData(data);
} else {
setState(() {
_isLoading = false;
});
ToastHelper.showErrorToast(context, 'Invalid OTP. Please try again');
// Show a Snackbar if the OTP is invalid
print('Invalid OTP. Please try again');
}
// Navigate to branch selection
Navigator.pushReplacement(
context,
MaterialPageRoute(
builder: (context) => BranchSelectionPage(),
),
);
// print('data: $data');
// _token = data['data'];
// String status = data['status'];
//
// // Directly access the post_enrollment data
// Map<String, dynamic> post = data['post_enrollment'];
// print('post: $post');
//
// _postToken = post['data'];
// String postStatus = post['status'];
//
// if (postStatus == 'success') {
// setState(() {
// _isLoading = false;
// });
// postSuccessData(post, data);
// } else if (status == 'success') {
// setState(() {
// _isLoading = false;
// });
// enrollmentSuccessData(data);
// } else {
// setState(() {
// _isLoading = false;
// });
// ToastHelper.showErrorToast(context, 'Invalid OTP. Please try again');
// // Show a Snackbar if the OTP is invalid
// print('Invalid OTP. Please try again');
// }
} else {
setState(() {
_isLoading = false;
@ -289,9 +313,17 @@ class _MyEmailVerifyState extends State<MyEmailVerify> {
Future<void> verifyMobileAndEmailNumber() async {
try {
// var enteredMobileNumber = mobileController.text;
final Map<String, dynamic> payload = (widget.type == 'mobile')
? {'mobile_number': widget.value}
: {'email': widget.value};
String apiEndpoint = (widget.type == 'mobile')
? Environment.apiUrl + 'verifyHrWithMobileNumber'
: Environment.apiUrl + 'verifyHrWithEmail';
final response = await http.post(
Uri.parse(Environment.apiUrl + 'verifyEmployeeEmailId'),
body: json.encode({'email': widget.email}),
Uri.parse(apiEndpoint),
body: json.encode(payload),
headers: {
HttpHeaders.contentTypeHeader: 'application/json',
},
@ -303,7 +335,7 @@ class _MyEmailVerifyState extends State<MyEmailVerify> {
String message = data['data']['message'];
if (userVerification) {
ToastHelper.showSuccessToast(
context, 'Verification code sent to ${widget.email}');
context, 'Verification code sent to ${widget.value}');
} else {
ToastHelper.showErrorToast(context, message);
print('Invalid mobile number');
@ -622,14 +654,17 @@ class _MyEmailVerifyState extends State<MyEmailVerify> {
textAlign: TextAlign.center,
text: TextSpan(
text:
"Please enter the one-time usage code sent to your mobile number ${widget.email}",
"Please enter the one-time usage code sent to your ",
style: TextStyle(
fontSize: 12,
height: 1.5,
color: Color(0xFF000000)),
children: [
TextSpan(
text: " (Change Email)",
text: widget.type == 'mobile' ? 'mobile number ${widget.value}' : 'Email Id ${widget.value}',
),
TextSpan(
text: widget.type == 'mobile' ? ' (Change Mobile)' : ' (Change Email)',
style: TextStyle(
fontSize: 12,
color: Color(

View File

@ -495,6 +495,9 @@ class _excelVerifyState extends State<excelVerify> {
});
handleImportAction();
} else {
setState(() {
isLoading = false;
});
ToastHelper.showErrorToast(context, data['message']);
print('Table');

View File

@ -12,6 +12,7 @@ import 'package:nhancepolicy/service/hrDashboardTabs/activePolicies.dart';
import 'package:nhancepolicy/service/hrDashboardTabs/cd.dart';
import 'package:nhancepolicy/service/hrDashboardTabs/claims.dart';
import 'package:nhancepolicy/service/hrDashboardTabs/preEnrollment.dart';
import 'package:nhancepolicy/service/token_storage_service.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:http/http.dart' as http;
import 'customAppBar/customAppBar.dart';
@ -75,6 +76,7 @@ class _hrDashboardState extends State<hrDashboard>
];
ScrollController _scrollController = ScrollController();
int selectedIndex = 0;
final tokenService = TokenStorageService();
@override
void initState() {
@ -94,6 +96,7 @@ class _hrDashboardState extends State<hrDashboard>
// isLoading = false;
// });
// });
}
@override
@ -104,11 +107,15 @@ class _hrDashboardState extends State<hrDashboard>
checkToken() async {
final SharedPreferences prefs = await SharedPreferences.getInstance();
enrollToken = prefs.getString('enrollToken');
_postToken = prefs.getString('_postToken');
// enrollToken = prefs.getString('pre_enrollment_data');
// _postToken = prefs.getString('post_enrollment_data');
// Get the current token
final token = await tokenService.getCurrentToken();
print('token - $token');
print('token check in');
if((enrollToken != null && enrollToken!.isNotEmpty) || (_postToken != null && _postToken!.isNotEmpty)){
if((token != null && token!.isNotEmpty)){
print('token check done');
_loadToken();
} else {
@ -120,17 +127,21 @@ class _hrDashboardState extends State<hrDashboard>
Future<void> _loadToken() async {
final SharedPreferences prefs = await SharedPreferences.getInstance();
_postToken = prefs.getString('_postToken');
enrollToken = prefs.getString('enrollToken');
enrollToken = await tokenService.getCurrentToken();
_postToken = await tokenService.getCurrentToken();
print(enrollToken);
print(_postToken);
if (enrollToken != null && enrollToken!.isNotEmpty) {
Map<String, dynamic>? decodedToken = Jwt.parseJwt(enrollToken!);
enrollmentClient_id = decodedToken['client_id'].toString();
// Map<String, dynamic>? decodedToken = Jwt.parseJwt(enrollToken!);
// enrollmentClient_id = decodedToken['client_id'].toString();
enrollmentClient_id = prefs.getString('enrollmentClient_id');
enrollmentEmpClientBranchId =
prefs.getString('enrollmentEmpClientBranchId');
enrollmentHrId = prefs.getString('enrollmentHrId');
print("Pre decodedToken - $decodedToken");
// print("Pre decodedToken - $decodedToken");
print("Pre enrollmentClient_id - $enrollmentClient_id");
print("Pre enrollmentEmpClientBranchId - $enrollmentEmpClientBranchId");
print("Pre enrollmentHrId - $enrollmentHrId");
@ -163,7 +174,9 @@ class _hrDashboardState extends State<hrDashboard>
clientLogo = prefs.getString('clientLogo');
clientName = prefs.getString('clientName');
} else {
getClientLogoAndDetails(
empClientId = prefs.getString('empClientId');
empClientBranchId = prefs.getString('empClientBranchId');
getClientLogoAndDetails(empClientId,empClientBranchId,
enrollmentEmpClientBranchId, enrollmentClient_id, enrollToken);
}
getCashDepositDetails(enrollmentEmpClientBranchId, enrollmentClient_id,
@ -171,14 +184,15 @@ class _hrDashboardState extends State<hrDashboard>
}
if (_postToken != null && _postToken!.isNotEmpty) {
Map<String, dynamic>? postdecodedToken = Jwt.parseJwt(_postToken!);
// Map<String, dynamic>? postdecodedToken = Jwt.parseJwt(_postToken!);
empClientId = postdecodedToken['client_id'].toString();
// empClientId = postdecodedToken['client_id'].toString();
empClientId = prefs.getString('empClientId');
empClientBranchId = prefs.getString('empClientBranchId');
empHrId = prefs.getString('empHrId');
print('post empHrId- $empHrId');
print('postdecodedToken- $postdecodedToken');
// print('postdecodedToken- $postdecodedToken');
print('post empClientId- $empClientId');
print('post empClientBranchId- $empClientBranchId');
@ -250,7 +264,10 @@ class _hrDashboardState extends State<hrDashboard>
clientLogo = prefs.getString('clientLogo');
clientName = prefs.getString('clientName');
} else {
getClientLogoAndDetails(empClientBranchId, empClientId, _postToken);
empClientId = prefs.getString('empClientId');
empClientBranchId = prefs.getString('empClientBranchId');
getClientLogoAndDetails(empClientId,empClientBranchId,
enrollmentEmpClientBranchId, enrollmentClient_id, _postToken);
}
print("getCashDepositDetails");
}
@ -344,9 +361,10 @@ class _hrDashboardState extends State<hrDashboard>
}).toList();
}
Future<void> getClientLogoAndDetails(clintBranchId, clintID, token) async {
Future<void> getClientLogoAndDetails(post_client_id, post_client_branch_id,
pre_client_branch_id, pre_client_id, token) async {
var url = Uri.parse(Environment.apiUrl +
'getClientDetails?client_id=$clintID&client_branch_id=$clintBranchId');
'getClientDetails?post_client_id=$post_client_id&post_branch_id=$post_client_branch_id&pre_client_id=$pre_client_id&pre_branch_id=$pre_client_branch_id');
try {
var response = await http.get(
url,
@ -525,6 +543,7 @@ class _hrDashboardState extends State<hrDashboard>
width: double.infinity,
// height: 75,
height: MediaQuery.of(context).size.height * 0.12,
padding: Responsive.isDesktop(context)
? const EdgeInsets.only(
top: 10, bottom: 10, left: 20, right: 20)
@ -542,32 +561,48 @@ class _hrDashboardState extends State<hrDashboard>
height: 80,
alignment: Alignment.centerLeft,
child: Image.network(
clientLogo ?? '',
width: 80, // Set the width here
height: 60, // Set the height here
loadingBuilder: (BuildContext context,
Widget child,
ImageChunkEvent? loadingProgress) {
if (loadingProgress == null) return child;
Uri.encodeFull(clientLogo ?? ''),
width: 200,
height: 200,
fit: BoxFit.contain,
frameBuilder: (BuildContext context, Widget child, int? frame, bool wasSynchronouslyLoaded) {
if (wasSynchronouslyLoaded) {
return child;
}
return AnimatedOpacity(
opacity: frame == null ? 0 : 1,
duration: const Duration(milliseconds: 500),
curve: Curves.easeOut,
child: child,
);
},
loadingBuilder: (BuildContext context, Widget child, ImageChunkEvent? loadingProgress) {
if (loadingProgress == null) {
return child;
}
return Center(
child: CircularProgressIndicator(
value: loadingProgress.expectedTotalBytes !=
null
? loadingProgress
.cumulativeBytesLoaded /
loadingProgress.expectedTotalBytes!
: null,
child: SizedBox(
width: 30,
height: 30,
child: CircularProgressIndicator(
strokeWidth: 2,
valueColor: AlwaysStoppedAnimation<Color>(Colors.grey),
value: loadingProgress.expectedTotalBytes != null
? loadingProgress.cumulativeBytesLoaded /
(loadingProgress.expectedTotalBytes ?? 1)
: null,
),
),
);
},
errorBuilder: (BuildContext context, Object error,
StackTrace? stackTrace) {
return Image.asset(
'assets/Solid_gray.png', // Replace 'default_image.png' with your default image asset path
width: 80,
height: 60,
fit: BoxFit.cover,
);
errorBuilder: (BuildContext context, Object error, StackTrace? stackTrace) {
return Text('');
// Image.asset(
// 'assets/Solid_gray.png',
// width: 80,
// height: 60,
// fit: BoxFit.cover,
// );
},
),
),

View File

@ -49,12 +49,12 @@ class _MyPhoneState extends State<MyHrLogin> {
clearLocalStorageWhenStarts(fromData) async {
print(fromData);
if(kIsWeb) {
// if(kIsWeb) {
print('vndbbcbdskbvkj3');
final SharedPreferences prefs = await SharedPreferences.getInstance();
prefs.clear();
print('Local Storage Clear');
}
// }
}
@ -99,8 +99,6 @@ class _MyPhoneState extends State<MyHrLogin> {
if (userVerification) {
final SharedPreferences prefs =
await SharedPreferences.getInstance();
// var enteredMobileNumber = mobileController.text;
// prefs.setString('empMobileNo', enteredMobileNumber);
if (isEmailFieldVisible) {
print('isEmailFieldVisible $isEmailFieldVisible');
prefs.setString('empEmail', emailController.text);
@ -111,12 +109,23 @@ class _MyPhoneState extends State<MyHrLogin> {
context,
MaterialPageRoute(
builder: (context) => MyEmailVerify(
email: emailController.text, // Pass the phone number
type: 'email',
value: emailController.text.trim(), // Pass the phone number
),
),
);
} else {
_verifyPhoneNumber();
ToastHelper.showSuccessToast(
context, 'Verification code sent to ${emailController.text}');
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => MyEmailVerify(
type: 'mobile',
value: mobileController.text.trim(), // Pass the phone number
),
),
);
}
// ToastHelper.showSuccessToast(context, message);
@ -144,129 +153,129 @@ class _MyPhoneState extends State<MyHrLogin> {
}
}
Future<void> _verifyPhoneNumber() async {
var enteredMobileNumber = mobileController.text;
var countryCode = countryController.text;
print('${countryCode + enteredMobileNumber}');
await _auth.verifyPhoneNumber(
phoneNumber: '${countryCode + enteredMobileNumber}',
timeout: const Duration(seconds: 60),
verificationCompleted: (PhoneAuthCredential credential) async {
await _auth.signInWithCredential(credential);
// ToastHelper.showSuccessToast(context, 'Verified Successfully!');
// setState(() {
// _isLoading = false;
// });
},
verificationFailed: (FirebaseAuthException e) {
print('Verification Failed: ${e.code} - ${e.message}');
String errorMessage;
if (e.code == 'invalid-app-credential') {
errorMessage = 'Invalid Credential. Please try again.';
} else if (e.code == 'invalid-phone-number') {
errorMessage = 'The provided phone number is not valid.';
} else if (e.code == 'too-many-requests') {
errorMessage = 'Too many requests. Try again later.';
} else {
errorMessage = 'Verification Failed: ${e.message}';
}
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(errorMessage),
duration: Duration(seconds: 2),
),
);
ToastHelper.showSuccessToast(context, errorMessage);
setState(() {
_isLoading = false;
});
},
codeSent: (String verificationId, int? resendToken) async {
setState(() {
_verificationId = verificationId;
_resendToken = resendToken;
});
// When getting the verificationId
SharedPreferences prefs = await SharedPreferences.getInstance();
prefs.setString('verificationId', _verificationId);
ToastHelper.showSuccessToast(
context, 'Verification code sent to ${enteredMobileNumber}');
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => MyHrVerify(
verificationId: _verificationId,
mobileNumber: enteredMobileNumber,
resendToken: _resendToken,
onResendCode: _resendCode, // Pass the phone number
),
),
);
setState(() {
_isLoading = false;
});
},
codeAutoRetrievalTimeout: (String verificationId) async {
setState(() {
_verificationId = verificationId;
});
final prefs = await SharedPreferences.getInstance();
await prefs.setString('verificationId', verificationId);
ToastHelper.showSuccessToast(context, 'Code auto-retrieval timed out.');
setState(() {
_isLoading = false;
});
},
);
}
void _resendCode(String mobileNumber, int? resendToken) async {
await _auth.verifyPhoneNumber(
phoneNumber: '${countryController.text + mobileNumber}',
timeout: const Duration(seconds: 60),
forceResendingToken: resendToken,
verificationCompleted: (PhoneAuthCredential credential) async {
await _auth.signInWithCredential(credential);
},
verificationFailed: (FirebaseAuthException e) {
if (e.code == 'invalid-phone-number') {
print('The provided phone number is not valid.');
}
},
codeSent: (String verificationId, int? resendToken) async {
final prefs = await SharedPreferences.getInstance();
await prefs.setString('verificationId', verificationId);
setState(() {
_verificationId = verificationId;
_resendToken = resendToken;
});
ToastHelper.showSuccessToast(
context, 'Verification code resent to ${mobileNumber}');
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => MyHrVerify(
verificationId: _verificationId,
mobileNumber: mobileNumber,
resendToken: _resendToken,
onResendCode: _resendCode,
),
),
);
},
codeAutoRetrievalTimeout: (String verificationId) async {
final prefs = await SharedPreferences.getInstance();
await prefs.setString('verificationId', verificationId);
setState(() {
_verificationId = verificationId;
});
},
);
}
// Future<void> _verifyPhoneNumber() async {
// var enteredMobileNumber = mobileController.text;
// var countryCode = countryController.text;
// print('${countryCode + enteredMobileNumber}');
// await _auth.verifyPhoneNumber(
// phoneNumber: '${countryCode + enteredMobileNumber}',
// timeout: const Duration(seconds: 60),
// verificationCompleted: (PhoneAuthCredential credential) async {
// await _auth.signInWithCredential(credential);
// // ToastHelper.showSuccessToast(context, 'Verified Successfully!');
// // setState(() {
// // _isLoading = false;
// // });
// },
// verificationFailed: (FirebaseAuthException e) {
// print('Verification Failed: ${e.code} - ${e.message}');
// String errorMessage;
// if (e.code == 'invalid-app-credential') {
// errorMessage = 'Invalid Credential. Please try again.';
// } else if (e.code == 'invalid-phone-number') {
// errorMessage = 'The provided phone number is not valid.';
// } else if (e.code == 'too-many-requests') {
// errorMessage = 'Too many requests. Try again later.';
// } else {
// errorMessage = 'Verification Failed: ${e.message}';
// }
// ScaffoldMessenger.of(context).showSnackBar(
// SnackBar(
// content: Text(errorMessage),
// duration: Duration(seconds: 2),
// ),
// );
//
// ToastHelper.showSuccessToast(context, errorMessage);
// setState(() {
// _isLoading = false;
// });
// },
// codeSent: (String verificationId, int? resendToken) async {
// setState(() {
// _verificationId = verificationId;
// _resendToken = resendToken;
// });
//
// // When getting the verificationId
// SharedPreferences prefs = await SharedPreferences.getInstance();
// prefs.setString('verificationId', _verificationId);
//
// ToastHelper.showSuccessToast(
// context, 'Verification code sent to ${enteredMobileNumber}');
//
// Navigator.push(
// context,
// MaterialPageRoute(
// builder: (context) => MyHrVerify(
// verificationId: _verificationId,
// mobileNumber: enteredMobileNumber,
// resendToken: _resendToken,
// onResendCode: _resendCode, // Pass the phone number
// ),
// ),
// );
// setState(() {
// _isLoading = false;
// });
// },
// codeAutoRetrievalTimeout: (String verificationId) async {
// setState(() {
// _verificationId = verificationId;
// });
// final prefs = await SharedPreferences.getInstance();
// await prefs.setString('verificationId', verificationId);
// ToastHelper.showSuccessToast(context, 'Code auto-retrieval timed out.');
// setState(() {
// _isLoading = false;
// });
// },
// );
// }
//
// void _resendCode(String mobileNumber, int? resendToken) async {
// await _auth.verifyPhoneNumber(
// phoneNumber: '${countryController.text + mobileNumber}',
// timeout: const Duration(seconds: 60),
// forceResendingToken: resendToken,
// verificationCompleted: (PhoneAuthCredential credential) async {
// await _auth.signInWithCredential(credential);
// },
// verificationFailed: (FirebaseAuthException e) {
// if (e.code == 'invalid-phone-number') {
// print('The provided phone number is not valid.');
// }
// },
// codeSent: (String verificationId, int? resendToken) async {
// final prefs = await SharedPreferences.getInstance();
// await prefs.setString('verificationId', verificationId);
// setState(() {
// _verificationId = verificationId;
// _resendToken = resendToken;
// });
// ToastHelper.showSuccessToast(
// context, 'Verification code resent to ${mobileNumber}');
// Navigator.push(
// context,
// MaterialPageRoute(
// builder: (context) => MyHrVerify(
// verificationId: _verificationId,
// mobileNumber: mobileNumber,
// resendToken: _resendToken,
// onResendCode: _resendCode,
// ),
// ),
// );
// },
// codeAutoRetrievalTimeout: (String verificationId) async {
// final prefs = await SharedPreferences.getInstance();
// await prefs.setString('verificationId', verificationId);
// setState(() {
// _verificationId = verificationId;
// });
// },
// );
// }
@override
Widget build(BuildContext context) {

View File

@ -4,6 +4,7 @@ import 'package:google_fonts/google_fonts.dart';
import 'package:nhancepolicy/customAppBar/toastHelper.dart';
import 'package:nhancepolicy/models/environment.dart';
import 'package:nhancepolicy/service/api_service.dart';
import 'package:nhancepolicy/service/token_storage_service.dart';
import 'package:pinput/pinput.dart';
import 'dart:async';
import 'package:flutter/gestures.dart';
@ -14,6 +15,8 @@ import 'package:shared_preferences/shared_preferences.dart';
import 'package:nhancepolicy/responsive.dart';
import 'package:jwt_decode/jwt_decode.dart';
import 'branch/branch_selection_page.dart';
class MyHrVerify extends StatefulWidget {
final String verificationId;
final String mobileNumber;
@ -139,35 +142,58 @@ class _MyVerifyState extends State<MyHrVerify> {
_isLoading = false;
});
Map<String, dynamic> data = json.decode(response.body);
print('data: $data');
_token = data['data'];
String status = data['status'];
// Directly access the post_enrollment data
Map<String, dynamic> post = data['post_enrollment'];
print('post: $post');
print('Response data: $data');
_postToken = post['data'];
String postStatus = post['status'];
// Extract enrollment data
List<dynamic> preEnrollmentData = data['data'] ?? [];
Map<String, dynamic> postEnrollment = data['post_enrollment'] ?? {};
List<dynamic> postEnrollmentData = postEnrollment['data'] ?? [];
if (postStatus == 'success') {
setState(() {
_isLoading = false;
});
postSuccessData(post, data);
} else if (status == 'success') {
setState(() {
_isLoading = false;
});
enrollmentSuccessData(data);
} else {
setState(() {
_isLoading = false;
});
ToastHelper.showErrorToast(context, 'Invalid OTP. Please try again');
// Show a Snackbar if the OTP is invalid
print('Invalid OTP. Please try again');
}
// Save to global storage
final tokenStorage = TokenStorageService();
await tokenStorage.saveEnrollmentData(
preEnrollmentData,
postEnrollmentData,
);
// Navigate to branch selection
Navigator.pushReplacement(
context,
MaterialPageRoute(
builder: (context) => BranchSelectionPage(),
),
);
// print('data: $data');
// _token = data['data'];
// String status = data['status'];
//
// // Directly access the post_enrollment data
// Map<String, dynamic> post = data['post_enrollment'];
// print('post: $post');
//
// _postToken = post['data'];
// String postStatus = post['status'];
//
// if (postStatus == 'success') {
// setState(() {
// _isLoading = false;
// });
// postSuccessData(post, data);
// } else if (status == 'success') {
// setState(() {
// _isLoading = false;
// });
// enrollmentSuccessData(data);
// } else {
// setState(() {
// _isLoading = false;
// });
// ToastHelper.showErrorToast(context, 'Invalid OTP. Please try again');
// // Show a Snackbar if the OTP is invalid
// print('Invalid OTP. Please try again');
// }
} else {
setState(() {
_isLoading = false;

View File

@ -2,6 +2,7 @@
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:nhancepolicy/addons.dart';
import 'package:nhancepolicy/branch/branch_selection_page.dart';
import 'package:nhancepolicy/empReview.dart';
import 'package:nhancepolicy/empDetails.dart';
import 'package:nhancepolicy/excel_verification.dart';
@ -10,6 +11,7 @@ import 'package:nhancepolicy/hrVerify.dart';
import 'package:nhancepolicy/models/environment.dart';
import 'package:nhancepolicy/phone.dart';
import 'package:nhancepolicy/postFileUpload.dart';
import 'package:nhancepolicy/service/token_storage_service.dart';
import 'package:nhancepolicy/verify.dart';
import 'package:nhancepolicy/home.dart';
import 'package:nhancepolicy/hrDashboard.dart';
@ -28,8 +30,23 @@ Future<void> main() async {
// print('Local Storage cleared by window');
// html.window.localStorage.clear();
// });
// Ask confirmation (browser may or may not show it again)
// html.window.onBeforeUnload.listen((event) {
// final e = event as html.BeforeUnloadEvent;
// e.preventDefault();
// e.returnValue = '';
// });
// Always clear when user leaves (refresh/close)
// html.window.onUnload.listen((event) {
// html.window.localStorage.clear();
// });
// await dotenv.load(fileName: Environment.fileName);
WidgetsFlutterBinding.ensureInitialized();
// Initialize token storage
await TokenStorageService().initialize();
await Firebase.initializeApp(
options: const FirebaseOptions(
apiKey: 'AIzaSyCSvDM5fG2blDBE69Cae3S-iYRwwNBy7xo',
@ -57,7 +74,9 @@ Future<void> main() async {
routes: {
'phone': (context) => MyPhone(),
'mailVerify': (context) => MyEmailVerify(
email: '',
type: '',
value: '',
),
'verify': (context) => MyVerify(
verificationId: '',
@ -130,6 +149,7 @@ Future<void> main() async {
cardPolicy_ExpDate: '',
),
'oldPolicy': (context) => oldPolicy(),
'branchSelection': (context) => BranchSelectionPage(),
},
));
}

View File

@ -19,8 +19,8 @@ class ApiService {
Future<void> _initializeToken() async {
final prefs = await SharedPreferences.getInstance();
_token = prefs.getString('token') ?? '';
final hrprefs = await SharedPreferences.getInstance();
_hrtoken = hrprefs.getString('hrtoken') ?? '';
// final hrprefs = await SharedPreferences.getInstance();
// _hrtoken = hrprefs.getString('hrtoken') ?? '';
}
Future<void> getTokenLoadAPI(token) async {

View File

@ -0,0 +1,157 @@
import 'package:shared_preferences/shared_preferences.dart';
import 'dart:convert';
class TokenStorageService {
static final TokenStorageService _instance = TokenStorageService._internal();
factory TokenStorageService() => _instance;
TokenStorageService._internal();
// Storage keys
static const String _preEnrollmentKey = 'pre_enrollment_data';
static const String _postEnrollmentKey = 'post_enrollment_data';
static const String _selectedBranchKey = 'selected_branch';
static const String _decodedTokenKey = 'decoded_token';
// In-memory cache
List<dynamic>? _preEnrollmentData;
List<dynamic>? _postEnrollmentData;
Map<String, dynamic>? _selectedBranch;
Map<String, dynamic>? _decodedToken;
// Initialize - Load data from storage
Future<void> initialize() async {
final prefs = await SharedPreferences.getInstance();
final preData = prefs.getString(_preEnrollmentKey);
if (preData != null) {
_preEnrollmentData = json.decode(preData);
}
final postData = prefs.getString(_postEnrollmentKey);
if (postData != null) {
_postEnrollmentData = json.decode(postData);
}
final branchData = prefs.getString(_selectedBranchKey);
if (branchData != null) {
_selectedBranch = json.decode(branchData);
}
final tokenData = prefs.getString(_decodedTokenKey);
if (tokenData != null) {
_decodedToken = json.decode(tokenData);
}
}
// Save enrollment data
Future<void> saveEnrollmentData(List<dynamic> preData, List<dynamic> postData) async {
_preEnrollmentData = preData;
_postEnrollmentData = postData;
final prefs = await SharedPreferences.getInstance();
await prefs.setString(_preEnrollmentKey, json.encode(preData));
await prefs.setString(_postEnrollmentKey, json.encode(postData));
}
// Get combined unique branches
List<Map<String, dynamic>> getCombinedBranches() {
List<Map<String, dynamic>> combined = [];
Set<String> seenTokens = {};
Set<String> seenIds = {};
// Add pre-enrollment data
if (_preEnrollmentData != null) {
for (var item in _preEnrollmentData!) {
String token = item['token']?.toString() ?? '';
String uniqueId = '${item['id']}_${item['client_id']}_${item['client_branch_id']}';
if (seenIds.contains(uniqueId)) continue;
if (token.isNotEmpty && seenTokens.contains(token)) continue;
if (token.isNotEmpty) seenTokens.add(token);
seenIds.add(uniqueId);
combined.add({...item, 'enrollment_type': 'pre'});
}
}
// Add post-enrollment data
if (_postEnrollmentData != null) {
for (var item in _postEnrollmentData!) {
String token = item['token']?.toString() ?? '';
String uniqueId = '${item['id']}_${item['client_id']}_${item['client_branch_id']}';
if (seenIds.contains(uniqueId)) continue;
if (token.isNotEmpty && seenTokens.contains(token)) continue;
if (token.isNotEmpty) seenTokens.add(token);
seenIds.add(uniqueId);
combined.add({...item, 'enrollment_type': 'post'});
}
}
return combined;
}
// Save selected branch and decode token
Future<void> saveSelectedBranch(Map<String, dynamic> branch) async {
_selectedBranch = branch;
String token = branch['token']?.toString() ?? '';
if (token.isNotEmpty) {
_decodedToken = _decodeJWT(token);
} else {
_decodedToken = null;
}
final prefs = await SharedPreferences.getInstance();
await prefs.setString(_selectedBranchKey, json.encode(branch));
if (_decodedToken != null) {
await prefs.setString(_decodedTokenKey, json.encode(_decodedToken));
}
}
// Decode JWT token
Map<String, dynamic>? _decodeJWT(String token) {
try {
final parts = token.split('.');
if (parts.length != 3) return null;
final payload = parts[1];
var normalized = base64Url.normalize(payload);
var decoded = utf8.decode(base64Url.decode(normalized));
return json.decode(decoded);
} catch (e) {
print('Error decoding token: $e');
return null;
}
}
// Getters
Map<String, dynamic>? getSelectedBranch() => _selectedBranch;
Map<String, dynamic>? getDecodedToken() => _decodedToken;
String? getCurrentToken() => _selectedBranch?['token'];
bool isLoggedIn() {
return _selectedBranch != null &&
_selectedBranch!['token'] != null &&
_selectedBranch!['token'].toString().isNotEmpty;
}
// Clear all data (logout)
Future<void> clearAll() async {
_preEnrollmentData = null;
_postEnrollmentData = null;
_selectedBranch = null;
_decodedToken = null;
final prefs = await SharedPreferences.getInstance();
await prefs.remove(_preEnrollmentKey);
await prefs.remove(_postEnrollmentKey);
await prefs.remove(_selectedBranchKey);
await prefs.remove(_decodedTokenKey);
}
}