merged
This commit is contained in:
commit
f1f3bcd1b6
@ -90,7 +90,7 @@ class _SplashScreenState extends State<SplashScreen> {
|
||||
Future<String?> tokenRedirectLogic(
|
||||
BuildContext context, GoRouterState state) async {
|
||||
print('ABCDEFGH');
|
||||
const allowedWithoutToken = [
|
||||
const guestRoutes = [
|
||||
'/login',
|
||||
'/verify',
|
||||
'/mailVerify',
|
||||
@ -98,17 +98,17 @@ Future<String?> tokenRedirectLogic(
|
||||
'/splash',
|
||||
];
|
||||
|
||||
final location = state.uri.toString();
|
||||
|
||||
// ✅ Allow login, verify, splash without token
|
||||
if (allowedWithoutToken.contains(location)) return null;
|
||||
|
||||
// ✅ Check if token exists
|
||||
// final hasToken = await TokenService.hasValidToken();
|
||||
// if (!hasToken) return '/login';
|
||||
final hasToken = await TokenService.hasValidToken();
|
||||
print('hasToken : $hasToken');
|
||||
if (!hasToken) {
|
||||
final location = state.matchedLocation;
|
||||
|
||||
// ✅ If user is already logged in, never allow returning to OTP/login screens.
|
||||
if (hasToken && guestRoutes.contains(location)) {
|
||||
return '/home';
|
||||
}
|
||||
|
||||
// ✅ If not logged in and trying to access a protected route, force login.
|
||||
if (!hasToken && !guestRoutes.contains(location)) {
|
||||
print('!!!!hasToken : $hasToken');
|
||||
await SessionManager().clear();
|
||||
// Show toast once
|
||||
|
||||
@ -28,72 +28,84 @@ class changesPassword extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _changesPasswordState extends State<changesPassword> {
|
||||
final TextEditingController oldPasswordController = TextEditingController();
|
||||
final TextEditingController newPasswordController = TextEditingController();
|
||||
final TextEditingController confirmPasswordController = TextEditingController();
|
||||
final _formKey = GlobalKey<FormState>();
|
||||
dynamic _preToken;
|
||||
dynamic _postToken;
|
||||
dynamic clientName;
|
||||
dynamic clientLogo;
|
||||
final TextEditingController oldPasswordController = TextEditingController();
|
||||
final TextEditingController newPasswordController = TextEditingController();
|
||||
final TextEditingController confirmPasswordController = TextEditingController();
|
||||
final _formKey = GlobalKey<FormState>();
|
||||
dynamic _preToken;
|
||||
dynamic _postToken;
|
||||
dynamic clientName;
|
||||
dynamic clientLogo;
|
||||
bool _isLoading = false;
|
||||
bool _obscureOldPassword = true;
|
||||
bool _obscureNewPassword = true;
|
||||
bool _obscureConfirmPassword = true;
|
||||
late SessionManager session;
|
||||
bool hasMinLength = false;
|
||||
bool hasUpperLower = false;
|
||||
bool hasNumber = false;
|
||||
bool hasSpecialChar = false;
|
||||
bool get isPasswordValid => hasMinLength && hasUpperLower && hasNumber && hasSpecialChar;
|
||||
bool hasMinLength = false;
|
||||
bool hasUpperLower = false;
|
||||
bool hasNumber = false;
|
||||
bool hasSpecialChar = false;
|
||||
bool get isPasswordValid => hasMinLength && hasUpperLower && hasNumber && hasSpecialChar;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
}
|
||||
|
||||
void validatePassword(String password) {
|
||||
setState(() {
|
||||
hasMinLength = password.length >= 8;
|
||||
hasUpperLower = RegExp(r'(?=.*[A-Za-z])').hasMatch(password);
|
||||
hasNumber = RegExp(r'(?=.*\d)').hasMatch(password);
|
||||
hasSpecialChar = RegExp(r'(?=.*[@$!%*#?&])').hasMatch(password);
|
||||
});
|
||||
}
|
||||
void validatePassword(String password) {
|
||||
setState(() {
|
||||
hasMinLength = password.length >= 8;
|
||||
hasUpperLower = RegExp(r'(?=.*[A-Za-z])').hasMatch(password);
|
||||
hasNumber = RegExp(r'(?=.*\d)').hasMatch(password);
|
||||
hasSpecialChar = RegExp(r'(?=.*[@$!%*#?&])').hasMatch(password);
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> resetYourPassword() async {
|
||||
final oldpassword = oldPasswordController.text.trim();
|
||||
final newPassword = newPasswordController.text.trim();
|
||||
final confirmPassword = confirmPasswordController.text.trim();
|
||||
try {
|
||||
if (_formKey.currentState!.validate()) {
|
||||
if (confirmPassword != newPassword) {
|
||||
ToastHelper.showErrorToast(context, 'Passwords do not match');
|
||||
return;
|
||||
}
|
||||
setState(() {
|
||||
_isLoading = true;
|
||||
});
|
||||
final oldpassword = oldPasswordController.text.trim();
|
||||
final newPassword = newPasswordController.text.trim();
|
||||
final confirmPassword = confirmPasswordController.text.trim();
|
||||
try {
|
||||
if (_formKey.currentState!.validate()) {
|
||||
// Extra safety checks before calling the API
|
||||
if (oldpassword == newPassword) {
|
||||
ToastHelper.showErrorToast(
|
||||
context, 'New password cannot be same as old password');
|
||||
return;
|
||||
}
|
||||
if (!isPasswordValid) {
|
||||
ToastHelper.showErrorToast(
|
||||
context,
|
||||
'Password must be 8+ characters with 1 letter, 1 number, and 1 special character');
|
||||
return;
|
||||
}
|
||||
if (confirmPassword != newPassword) {
|
||||
ToastHelper.showErrorToast(context, 'Passwords do not match');
|
||||
return;
|
||||
}
|
||||
setState(() {
|
||||
_isLoading = true;
|
||||
});
|
||||
|
||||
// Determine the API and the payload based on the visible field
|
||||
String apiEndpoint = Environment.apiUrlEnrollment + 'changePassword';
|
||||
Map<String, dynamic> payload = {
|
||||
'email_id': widget.email,
|
||||
'client_id': widget.client_id,
|
||||
'old_password': oldpassword,
|
||||
'new_password': newPassword,
|
||||
'confirm_password': confirmPassword
|
||||
};
|
||||
// Determine the API and the payload based on the visible field
|
||||
String apiEndpoint = Environment.apiUrlEnrollment + 'changePassword';
|
||||
Map<String, dynamic> payload = {
|
||||
'email_id': widget.email,
|
||||
'client_id': widget.client_id,
|
||||
'old_password': oldpassword,
|
||||
'new_password': newPassword,
|
||||
'confirm_password': confirmPassword
|
||||
};
|
||||
|
||||
// var enteredMobileNumber = mobileController.text;
|
||||
final response = await http.post(
|
||||
Uri.parse(apiEndpoint),
|
||||
body: json.encode(payload),
|
||||
headers: {
|
||||
HttpHeaders.contentTypeHeader: 'application/json',
|
||||
'APP-SIGNATURE': 'nhance-2025-signature-T8f6gV9k5fK2wf5V5c4vId3b0J8z6cAm2x8Y',
|
||||
},
|
||||
);
|
||||
// var enteredMobileNumber = mobileController.text;
|
||||
final response = await http.post(
|
||||
Uri.parse(apiEndpoint),
|
||||
body: json.encode(payload),
|
||||
headers: {
|
||||
HttpHeaders.contentTypeHeader: 'application/json',
|
||||
'APP-SIGNATURE': 'nhance-2025-signature-T8f6gV9k5fK2wf5V5c4vId3b0J8z6cAm2x8Y',
|
||||
},
|
||||
);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
Map<String, dynamic> data = json.decode(response.body);
|
||||
@ -163,26 +175,26 @@ class _changesPasswordState extends State<changesPassword> {
|
||||
print('Error: $e');
|
||||
}
|
||||
|
||||
// 🔹 Password validation
|
||||
// if (password.isEmpty) {
|
||||
// ToastHelper.showErrorToast(context, 'Please enter your password');
|
||||
// return;
|
||||
// }
|
||||
// if (password.length < 6) {
|
||||
// ToastHelper.showErrorToast(context, 'Password must be at least 6 characters');
|
||||
// return;
|
||||
// }
|
||||
// if (!RegExp(r'^(?=.*[A-Z])(?=.*[0-9]).{6,}$').hasMatch(password)) {
|
||||
// ToastHelper.showErrorToast(context, 'Include at least 1 uppercase letter and 1 number');
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// // 🔹 Confirm password validation
|
||||
// if (confirmPassword.isEmpty) {
|
||||
// ToastHelper.showErrorToast(context, 'Please confirm your password');
|
||||
// return;
|
||||
// }
|
||||
}
|
||||
// 🔹 Password validation
|
||||
// if (password.isEmpty) {
|
||||
// ToastHelper.showErrorToast(context, 'Please enter your password');
|
||||
// return;
|
||||
// }
|
||||
// if (password.length < 6) {
|
||||
// ToastHelper.showErrorToast(context, 'Password must be at least 6 characters');
|
||||
// return;
|
||||
// }
|
||||
// if (!RegExp(r'^(?=.*[A-Z])(?=.*[0-9]).{6,}$').hasMatch(password)) {
|
||||
// ToastHelper.showErrorToast(context, 'Include at least 1 uppercase letter and 1 number');
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// // 🔹 Confirm password validation
|
||||
// if (confirmPassword.isEmpty) {
|
||||
// ToastHelper.showErrorToast(context, 'Please confirm your password');
|
||||
// return;
|
||||
// }
|
||||
}
|
||||
|
||||
//Ends Login with UserName and Password
|
||||
|
||||
@ -227,7 +239,7 @@ class _changesPasswordState extends State<changesPassword> {
|
||||
body: SingleChildScrollView(
|
||||
keyboardDismissBehavior: ScrollViewKeyboardDismissBehavior.onDrag,
|
||||
child: Container(
|
||||
height: _size.height,
|
||||
constraints: BoxConstraints(minHeight: _size.height),
|
||||
color: Colors.white,
|
||||
child: Stack(
|
||||
children: [
|
||||
@ -314,7 +326,7 @@ class _changesPasswordState extends State<changesPassword> {
|
||||
// ),
|
||||
Container(
|
||||
margin: marginInsets,
|
||||
alignment: Alignment.bottomCenter,
|
||||
alignment: Alignment.topCenter,
|
||||
child: SingleChildScrollView(
|
||||
child: Form(
|
||||
key: _formKey,
|
||||
@ -332,62 +344,37 @@ class _changesPasswordState extends State<changesPassword> {
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.center,
|
||||
children: [
|
||||
if (!Responsive.isMobile(context) &&
|
||||
!Responsive.isTablet(context))
|
||||
// Desktop-only: center "<" and logo in one row
|
||||
if (Responsive.isDesktop(context))
|
||||
Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.center,
|
||||
children: [
|
||||
InkWell(
|
||||
onTap: () {
|
||||
context.go('/home');
|
||||
},
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
// if (!Responsive.isDesktop(context))
|
||||
Icon(
|
||||
Icons.chevron_left,
|
||||
color: Color(0xFF000000),
|
||||
size: 30,
|
||||
),
|
||||
SizedBox(
|
||||
width: Responsive.isDesktop(context)
|
||||
? 0
|
||||
: 5),
|
||||
],
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
flex: 12,
|
||||
child: Align(
|
||||
alignment: Alignment
|
||||
.topLeft, // ✅ Always top-left
|
||||
child: _size.width <= 1100
|
||||
? Image.asset(
|
||||
'assets/nhance_app_logo.png',
|
||||
width: 150,
|
||||
height: 150,
|
||||
)
|
||||
: _size.width > 1100
|
||||
? Image.asset(
|
||||
'assets/nhance_app_logo.png',
|
||||
width: 150,
|
||||
height: 150,
|
||||
)
|
||||
: Image.asset(
|
||||
'assets/nhance_app_logo.png',
|
||||
width: 150,
|
||||
height: 150,
|
||||
),
|
||||
onTap: () {
|
||||
context.go('/home');
|
||||
},
|
||||
child: const Icon(
|
||||
Icons.chevron_left,
|
||||
color: Color(0xFF000000),
|
||||
size: 30,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Image.asset(
|
||||
'assets/nhance_app_logo.png',
|
||||
width: 150,
|
||||
height: 150,
|
||||
// color: Colors.green,
|
||||
),
|
||||
],
|
||||
),
|
||||
SizedBox(
|
||||
height: Responsive.isDesktop(context)
|
||||
? _size.height * 0.1
|
||||
? null
|
||||
: 10,
|
||||
),
|
||||
SizedBox(height: 10),
|
||||
// SizedBox(height: 10),
|
||||
Container(
|
||||
margin: Responsive.isDesktop(context)
|
||||
? EdgeInsets.symmetric(
|
||||
@ -437,259 +424,278 @@ class _changesPasswordState extends State<changesPassword> {
|
||||
height: 20,
|
||||
),
|
||||
|
||||
Column(
|
||||
children: [
|
||||
// 🔹 Password Field
|
||||
Container(
|
||||
height: 55,
|
||||
margin: Responsive.isDesktop(
|
||||
context)
|
||||
? const EdgeInsets
|
||||
Column(
|
||||
children: [
|
||||
// 🔹 Password Field
|
||||
Container(
|
||||
height: 55,
|
||||
margin: Responsive.isDesktop(
|
||||
context)
|
||||
? const EdgeInsets
|
||||
.symmetric(
|
||||
horizontal: 150)
|
||||
: const EdgeInsets
|
||||
.symmetric(
|
||||
horizontal: 0),
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(
|
||||
width: 1,
|
||||
color: Colors.grey),
|
||||
borderRadius:
|
||||
BorderRadius.circular(
|
||||
10),
|
||||
),
|
||||
child: TextFormField(
|
||||
controller:
|
||||
oldPasswordController,
|
||||
obscureText:
|
||||
_obscureOldPassword,
|
||||
textAlignVertical:
|
||||
TextAlignVertical
|
||||
.center,
|
||||
decoration: InputDecoration(
|
||||
border: InputBorder.none,
|
||||
hintText:
|
||||
"Old Password",
|
||||
contentPadding:
|
||||
const EdgeInsets
|
||||
.symmetric(
|
||||
horizontal: 150)
|
||||
: const EdgeInsets
|
||||
.symmetric(
|
||||
horizontal: 0),
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(
|
||||
width: 1,
|
||||
color: Colors.grey),
|
||||
borderRadius:
|
||||
BorderRadius.circular(
|
||||
10),
|
||||
),
|
||||
child: TextFormField(
|
||||
controller:
|
||||
oldPasswordController,
|
||||
obscureText:
|
||||
_obscureOldPassword,
|
||||
textAlignVertical:
|
||||
TextAlignVertical
|
||||
.center,
|
||||
decoration: InputDecoration(
|
||||
border: InputBorder.none,
|
||||
hintText:
|
||||
"Old Password",
|
||||
contentPadding:
|
||||
const EdgeInsets
|
||||
.symmetric(
|
||||
horizontal: 10),
|
||||
suffixIcon: IconButton(
|
||||
icon: Icon(
|
||||
_obscureOldPassword
|
||||
? Icons
|
||||
.visibility_off
|
||||
: Icons
|
||||
.visibility,
|
||||
color: Colors.grey,
|
||||
),
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
_obscureOldPassword =
|
||||
!_obscureOldPassword;
|
||||
});
|
||||
},
|
||||
),
|
||||
horizontal: 10),
|
||||
suffixIcon: IconButton(
|
||||
icon: Icon(
|
||||
_obscureOldPassword
|
||||
? Icons
|
||||
.visibility_off
|
||||
: Icons
|
||||
.visibility,
|
||||
color: Colors.grey,
|
||||
),
|
||||
validator: (value) {
|
||||
if (value == null ||
|
||||
value.isEmpty) {
|
||||
return 'Please enter your old password';
|
||||
}
|
||||
if (value.length < 6) {
|
||||
return 'Password must be at least 6 characters';
|
||||
}
|
||||
if (!RegExp(
|
||||
r'^(?=.*[A-Z])(?=.*[0-9]).{6,}$')
|
||||
.hasMatch(value)) {
|
||||
return 'Include at least 1 uppercase letter and 1 number';
|
||||
}
|
||||
return null;
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
_obscureOldPassword =
|
||||
!_obscureOldPassword;
|
||||
});
|
||||
},
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
Container(
|
||||
height: 55,
|
||||
margin: Responsive.isDesktop(context)
|
||||
? const EdgeInsets.symmetric(horizontal: 150)
|
||||
: const EdgeInsets.symmetric(horizontal: 0),
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(width: 1, color: Colors.grey),
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
child: TextFormField(
|
||||
controller: newPasswordController,
|
||||
obscureText: _obscureNewPassword,
|
||||
onChanged: validatePassword,
|
||||
textAlignVertical: TextAlignVertical.center,
|
||||
decoration: InputDecoration(
|
||||
border: InputBorder.none,
|
||||
hintText: "New Password",
|
||||
contentPadding: const EdgeInsets.symmetric(horizontal: 10),
|
||||
suffixIcon: IconButton(
|
||||
icon: Icon(
|
||||
_obscureNewPassword ? Icons.visibility_off : Icons.visibility,
|
||||
color: Colors.grey,
|
||||
),
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
_obscureNewPassword = !_obscureNewPassword;
|
||||
});
|
||||
},
|
||||
),
|
||||
validator: (value) {
|
||||
if (value == null ||
|
||||
value.isEmpty) {
|
||||
return 'Please enter your old password';
|
||||
}
|
||||
if (value.length < 8) {
|
||||
return 'Password must be at least 8 characters';
|
||||
}
|
||||
if (!RegExp(
|
||||
r'^(?=.*[A-Z])(?=.*[0-9]).{6,}$')
|
||||
.hasMatch(value)) {
|
||||
return 'Include at least 1 uppercase letter and 1 number';
|
||||
}
|
||||
return null;
|
||||
},
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
Container(
|
||||
height: 55,
|
||||
margin: Responsive.isDesktop(context)
|
||||
? const EdgeInsets.symmetric(horizontal: 150)
|
||||
: const EdgeInsets.symmetric(horizontal: 0),
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(width: 1, color: Colors.grey),
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
child: TextFormField(
|
||||
controller: newPasswordController,
|
||||
obscureText: _obscureNewPassword,
|
||||
onChanged: validatePassword,
|
||||
textAlignVertical: TextAlignVertical.center,
|
||||
decoration: InputDecoration(
|
||||
border: InputBorder.none,
|
||||
hintText: "New Password",
|
||||
contentPadding: const EdgeInsets.symmetric(horizontal: 10),
|
||||
suffixIcon: IconButton(
|
||||
icon: Icon(
|
||||
_obscureNewPassword ? Icons.visibility_off : Icons.visibility,
|
||||
color: Colors.grey,
|
||||
),
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
_obscureNewPassword = !_obscureNewPassword;
|
||||
});
|
||||
},
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
// 🔹 VALIDATION LIST
|
||||
Padding(
|
||||
padding: Responsive.isDesktop(context)
|
||||
? const EdgeInsets.symmetric(horizontal: 150)
|
||||
: const EdgeInsets.symmetric(horizontal: 5),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
validator: (value) {
|
||||
final password = value ?? '';
|
||||
if (password.isEmpty) {
|
||||
return 'Please enter your new password';
|
||||
}
|
||||
if (password.length < 8) {
|
||||
return 'Password must be at least 8 characters';
|
||||
}
|
||||
if (!RegExp(r'[A-Za-z]').hasMatch(password)) {
|
||||
return 'Password must include at least 1 letter';
|
||||
}
|
||||
if (!RegExp(r'\d').hasMatch(password)) {
|
||||
return 'Password must include at least 1 number';
|
||||
}
|
||||
if (!RegExp(r'[@$!%*#?&]').hasMatch(password)) {
|
||||
return 'Password must include at least 1 special character';
|
||||
}
|
||||
return null;
|
||||
},
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
// 🔹 VALIDATION LIST
|
||||
Padding(
|
||||
padding: Responsive.isDesktop(context)
|
||||
? const EdgeInsets.symmetric(horizontal: 150)
|
||||
: const EdgeInsets.symmetric(horizontal: 5),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Expanded(child: _buildCheckItem(hasMinLength, "Minimum 8 characters")),
|
||||
Expanded(child: _buildCheckItem(hasSpecialChar, "1 special character")),
|
||||
],
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
Expanded(child: _buildCheckItem(hasUpperLower, "1 UPPER or lower case")),
|
||||
Expanded(child: _buildCheckItem(hasNumber, "1 numerical")),
|
||||
],
|
||||
),
|
||||
Expanded(child: _buildCheckItem(hasMinLength, "Minimum 8 characters")),
|
||||
Expanded(child: _buildCheckItem(hasSpecialChar, "1 special character")),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
Container(
|
||||
height: 55,
|
||||
margin: Responsive.isDesktop(
|
||||
context)
|
||||
? const EdgeInsets
|
||||
.symmetric(
|
||||
horizontal: 150)
|
||||
: const EdgeInsets
|
||||
.symmetric(
|
||||
horizontal: 0),
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(
|
||||
width: 1,
|
||||
color: Colors.grey),
|
||||
borderRadius:
|
||||
BorderRadius.circular(
|
||||
10),
|
||||
Row(
|
||||
children: [
|
||||
Expanded(child: _buildCheckItem(hasUpperLower, "1 UPPER or lower case")),
|
||||
Expanded(child: _buildCheckItem(hasNumber, "1 numerical")),
|
||||
],
|
||||
),
|
||||
child: TextFormField(
|
||||
controller:
|
||||
confirmPasswordController,
|
||||
obscureText:
|
||||
_obscureConfirmPassword,
|
||||
textAlignVertical:
|
||||
TextAlignVertical
|
||||
.center,
|
||||
decoration: InputDecoration(
|
||||
border: InputBorder.none,
|
||||
hintText:
|
||||
"Confirm Password",
|
||||
contentPadding:
|
||||
const EdgeInsets
|
||||
.symmetric(
|
||||
horizontal: 10),
|
||||
suffixIcon: IconButton(
|
||||
icon: Icon(
|
||||
_obscureConfirmPassword
|
||||
? Icons
|
||||
.visibility_off
|
||||
: Icons
|
||||
.visibility,
|
||||
color: Colors.grey,
|
||||
),
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
_obscureConfirmPassword =
|
||||
!_obscureConfirmPassword;
|
||||
});
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
Container(
|
||||
height: 55,
|
||||
margin: Responsive.isDesktop(
|
||||
context)
|
||||
? const EdgeInsets
|
||||
.symmetric(
|
||||
horizontal: 150)
|
||||
: const EdgeInsets
|
||||
.symmetric(
|
||||
horizontal: 0),
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(
|
||||
width: 1,
|
||||
color: Colors.grey),
|
||||
borderRadius:
|
||||
BorderRadius.circular(
|
||||
10),
|
||||
),
|
||||
child: TextFormField(
|
||||
controller:
|
||||
confirmPasswordController,
|
||||
obscureText:
|
||||
_obscureConfirmPassword,
|
||||
textAlignVertical:
|
||||
TextAlignVertical
|
||||
.center,
|
||||
decoration: InputDecoration(
|
||||
border: InputBorder.none,
|
||||
hintText:
|
||||
"Confirm Password",
|
||||
contentPadding:
|
||||
const EdgeInsets
|
||||
.symmetric(
|
||||
horizontal: 10),
|
||||
suffixIcon: IconButton(
|
||||
icon: Icon(
|
||||
_obscureConfirmPassword
|
||||
? Icons
|
||||
.visibility_off
|
||||
: Icons
|
||||
.visibility,
|
||||
color: Colors.grey,
|
||||
),
|
||||
validator: (value) {
|
||||
if (value == null ||
|
||||
value.isEmpty) {
|
||||
return 'Please confirm your password';
|
||||
}
|
||||
if (value !=
|
||||
newPasswordController
|
||||
.text) {
|
||||
return 'Passwords do not match';
|
||||
}
|
||||
return null;
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
_obscureConfirmPassword =
|
||||
!_obscureConfirmPassword;
|
||||
});
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
validator: (value) {
|
||||
if (value == null ||
|
||||
value.isEmpty) {
|
||||
return 'Please confirm your password';
|
||||
}
|
||||
if (value !=
|
||||
newPasswordController
|
||||
.text) {
|
||||
return 'Passwords do not match';
|
||||
}
|
||||
return null;
|
||||
},
|
||||
),
|
||||
),
|
||||
SizedBox(height: 15),
|
||||
Container(
|
||||
margin:
|
||||
Responsive.isDesktop(context)
|
||||
? EdgeInsets.symmetric(
|
||||
horizontal: 150)
|
||||
: EdgeInsets.symmetric(
|
||||
horizontal: 0),
|
||||
child: SizedBox(
|
||||
width: double.infinity,
|
||||
height: 40,
|
||||
child: ElevatedButton(
|
||||
style:
|
||||
ElevatedButton.styleFrom(
|
||||
backgroundColor:
|
||||
Color(0xFF00989E),
|
||||
shape:
|
||||
RoundedRectangleBorder(
|
||||
borderRadius:
|
||||
BorderRadius.circular(
|
||||
10),
|
||||
),
|
||||
),
|
||||
onPressed: _isLoading
|
||||
? null
|
||||
: resetYourPassword,
|
||||
],
|
||||
),
|
||||
SizedBox(height: 15),
|
||||
Container(
|
||||
margin:
|
||||
Responsive.isDesktop(context)
|
||||
? EdgeInsets.symmetric(
|
||||
horizontal: 150)
|
||||
: EdgeInsets.symmetric(
|
||||
horizontal: 0),
|
||||
child: SizedBox(
|
||||
width: double.infinity,
|
||||
height: 40,
|
||||
child: ElevatedButton(
|
||||
style:
|
||||
ElevatedButton.styleFrom(
|
||||
backgroundColor:
|
||||
Color(0xFF00989E),
|
||||
shape:
|
||||
RoundedRectangleBorder(
|
||||
borderRadius:
|
||||
BorderRadius.circular(
|
||||
10),
|
||||
),
|
||||
),
|
||||
onPressed: _isLoading
|
||||
? null
|
||||
: resetYourPassword,
|
||||
|
||||
child: _isLoading
|
||||
? CircularProgressIndicator(
|
||||
valueColor:
|
||||
AlwaysStoppedAnimation<
|
||||
Color>(
|
||||
Color(0xFF00989E),
|
||||
),
|
||||
)
|
||||
: Text(
|
||||
"Reset Password",
|
||||
style: GoogleFonts
|
||||
.poppins(
|
||||
color: Color(
|
||||
0xFFFFFFFF),
|
||||
),
|
||||
),
|
||||
child: _isLoading
|
||||
? CircularProgressIndicator(
|
||||
valueColor:
|
||||
AlwaysStoppedAnimation<
|
||||
Color>(
|
||||
Color(0xFF00989E),
|
||||
),
|
||||
)
|
||||
: Text(
|
||||
"Reset Password",
|
||||
style: GoogleFonts
|
||||
.poppins(
|
||||
color: Color(
|
||||
0xFFFFFFFF),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
SizedBox(
|
||||
height: Responsive.isDesktop(context)
|
||||
? _size.height * 0.3
|
||||
? null
|
||||
: _size.height * 0.2,
|
||||
),
|
||||
// SizedBox(
|
||||
// height: _size.height * 0.1,
|
||||
// ),
|
||||
Container(
|
||||
alignment: Alignment.bottomCenter,
|
||||
alignment: Alignment.center,
|
||||
padding:
|
||||
EdgeInsets.symmetric(vertical: 8),
|
||||
child: RichText(
|
||||
@ -801,27 +807,27 @@ class _changesPasswordState extends State<changesPassword> {
|
||||
)));
|
||||
}
|
||||
|
||||
Widget _buildCheckItem(bool status, String text) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 2),
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(
|
||||
status ? Icons.check : Icons.close,
|
||||
color: status ? Colors.green : Colors.red,
|
||||
size: 18,
|
||||
),
|
||||
const SizedBox(width: 6),
|
||||
Text(
|
||||
text,
|
||||
style: TextStyle(
|
||||
color: status ? Colors.green : Colors.red,
|
||||
fontSize: 14,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
Widget _buildCheckItem(bool status, String text) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 2),
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(
|
||||
status ? Icons.check : Icons.close,
|
||||
color: status ? Colors.green : Colors.red,
|
||||
size: 18,
|
||||
),
|
||||
const SizedBox(width: 6),
|
||||
Text(
|
||||
text,
|
||||
style: TextStyle(
|
||||
color: status ? Colors.green : Colors.red,
|
||||
fontSize: 14,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -32,171 +32,216 @@
|
||||
// import '../session/settingUpPinAndBiometric.dart';
|
||||
// import '../verify.dart';
|
||||
//
|
||||
// class AppRouter {
|
||||
// // static final SessionNotifier sessionNotifier = SessionNotifier();
|
||||
// static GoRouter createRouter() {
|
||||
// return GoRouter(
|
||||
// initialLocation: kIsWeb ? '/login' : '/splash',
|
||||
// debugLogDiagnostics: true,
|
||||
// // refreshListenable: sessionNotifier,
|
||||
// redirect: (BuildContext context, GoRouterState state) {
|
||||
// const allowedWithoutToken = [
|
||||
// '/login',
|
||||
// '/verify',
|
||||
// '/mailVerify',
|
||||
// '/pinPage',
|
||||
// '/splash',
|
||||
// ];
|
||||
//
|
||||
// final location = state.uri.toString();
|
||||
// if (allowedWithoutToken.contains(location)) return null;
|
||||
//
|
||||
// // final hasToken = TokenService.hasValidTokenSync();
|
||||
// // if (!hasToken) return '/login';
|
||||
//
|
||||
// // if (!kIsWeb &&
|
||||
// // SessionManager().prefs?.getString('is_mpin_skipped') == '0' &&
|
||||
// // location != '/pinPage') {
|
||||
// // return '/pinPage';
|
||||
// // }
|
||||
//
|
||||
// return null;
|
||||
// },
|
||||
//
|
||||
//
|
||||
//
|
||||
// routes: [
|
||||
// // if (!kIsWeb)
|
||||
// // GoRoute(
|
||||
// // path: '/splash',
|
||||
// // builder: (context, state) => SplashScreen(),
|
||||
// // ),
|
||||
// GoRoute(
|
||||
// path: '/login',
|
||||
// builder: (context, state) => login(),
|
||||
// ),
|
||||
// GoRoute(
|
||||
// path: '/mailVerify',
|
||||
// builder: (context, state) {
|
||||
// final email = state.extra as String;
|
||||
// return MyEmailVerify(email: email);
|
||||
// },
|
||||
// ),
|
||||
// GoRoute(
|
||||
// path: '/verify',
|
||||
// builder: (context, state) {
|
||||
// final args = state.extra as Map<String, dynamic>;
|
||||
// return MyVerify(
|
||||
// verificationId: args['verificationId'] as String,
|
||||
// mobileNumber: args['mobileNumber'] as String,
|
||||
// resendToken: args['resendToken'],
|
||||
// onResendCode: args['onResendCode'] as Function(String, int?),
|
||||
// );
|
||||
// },
|
||||
// ),
|
||||
// GoRoute(
|
||||
// path: '/mailVerify',
|
||||
// builder: (context, state) => MyVerify(
|
||||
// verificationId: '',
|
||||
// mobileNumber: '',
|
||||
// resendToken: null,
|
||||
// onResendCode: (String, int) {},
|
||||
// ),
|
||||
// ),
|
||||
// GoRoute(
|
||||
// path: '/home',
|
||||
// builder: (context, state) => Home(),
|
||||
// ),
|
||||
// GoRoute(
|
||||
// path: '/pinSettingPage',
|
||||
// builder: (context, state) => pinSettingPage(),
|
||||
// ),
|
||||
// GoRoute(
|
||||
// path: '/pinPage',
|
||||
// builder: (context, state) => pinPage(),
|
||||
// ),
|
||||
// GoRoute(
|
||||
// path: '/changePin',
|
||||
// builder: (context, state) => changePin(),
|
||||
// ),
|
||||
// GoRoute(
|
||||
// path: '/claimprocess',
|
||||
// builder: (context, state) => claimprocess(),
|
||||
// ),
|
||||
// GoRoute(
|
||||
// path: '/policies',
|
||||
// builder: (context, state) => policies(),
|
||||
// ),
|
||||
// GoRoute(
|
||||
// path: '/claims',
|
||||
// builder: (context, state) => claims(),
|
||||
// ),
|
||||
// GoRoute(
|
||||
// path: '/profile',
|
||||
// builder: (context, state) => profile(),
|
||||
// ),
|
||||
// GoRoute(
|
||||
// path: '/help',
|
||||
// builder: (context, state) => help(),
|
||||
// ),
|
||||
// GoRoute(
|
||||
// path: '/wellness',
|
||||
// builder: (context, state) => wellness(),
|
||||
// ),
|
||||
// GoRoute(
|
||||
// path: '/planclaimsform',
|
||||
// builder: (context, state) => planclaimsform(),
|
||||
// ),
|
||||
// GoRoute(
|
||||
// path: '/privacypolicy',
|
||||
// builder: (context, state) => privacypolicy(),
|
||||
// ),
|
||||
// GoRoute(
|
||||
// path: '/termsofuse',
|
||||
// builder: (context, state) => termsofuse(),
|
||||
// ),
|
||||
// GoRoute(
|
||||
// path: '/generalExclusionsDeductibles',
|
||||
// builder: (context, state) => generalExclusionsDeductibles(),
|
||||
// ),
|
||||
// GoRoute(
|
||||
// path: '/chatbot()',
|
||||
// builder: (context, state) => chatbot(),
|
||||
// ),
|
||||
// GoRoute(
|
||||
// path: '/chatbot()',
|
||||
// builder: (context, state) => chatbot(),
|
||||
// ),
|
||||
// GoRoute(
|
||||
// path: '/tickettracklist',
|
||||
// builder: (context, state) => tickettracklist(
|
||||
// ticketID: "",
|
||||
// ),
|
||||
// ),
|
||||
// GoRoute(
|
||||
// path: '/empDetails()',
|
||||
// builder: (context, state) => empDetails(),
|
||||
// ),
|
||||
// GoRoute(
|
||||
// path: '/addOnsDetails()',
|
||||
// builder: (context, state) => addOnsDetails(),
|
||||
// ),
|
||||
// GoRoute(
|
||||
// path: '/empReviewDetails()',
|
||||
// builder: (context, state) => empReviewDetails(),
|
||||
// ),
|
||||
// GoRoute(
|
||||
// path: '/tickets()',
|
||||
// builder: (context, state) => tickets(),
|
||||
// ),
|
||||
// GoRoute(
|
||||
// path: '/raisedTicketHistory()',
|
||||
// builder: (context, state) => raisedTicketHistory(),
|
||||
// ),
|
||||
// ],
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
// import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import '../../main.dart';
|
||||
import '../email_verify.dart';
|
||||
import '../enrollment/addons.dart';
|
||||
import '../enrollment/empDetails.dart';
|
||||
import '../enrollment/empReview.dart';
|
||||
import '../login.dart';
|
||||
import '../postEnrollment/chatbot.dart';
|
||||
import '../postEnrollment/claimprocess.dart';
|
||||
import '../postEnrollment/claims.dart';
|
||||
import '../postEnrollment/faqs.dart';
|
||||
import '../postEnrollment/generalexclusionsdeductibles.dart';
|
||||
import '../postEnrollment/help.dart';
|
||||
import '../postEnrollment/home.dart';
|
||||
import '../postEnrollment/planclaimsform.dart';
|
||||
import '../postEnrollment/policies.dart';
|
||||
import '../postEnrollment/privacypolicy.dart';
|
||||
import '../postEnrollment/profile.dart';
|
||||
import '../postEnrollment/raisedTicketList.dart';
|
||||
import '../postEnrollment/retailClaimForm.dart';
|
||||
import '../postEnrollment/termsofuse.dart';
|
||||
import '../postEnrollment/tickets.dart';
|
||||
import '../postEnrollment/tickettracklist.dart';
|
||||
import '../postEnrollment/wellness.dart';
|
||||
import '../postEnrollment/wellness_web_view.dart';
|
||||
import '../service/SessionManager.dart';
|
||||
import '../service/TokenService.dart';
|
||||
import '../session/SetPinBiometric.dart';
|
||||
import '../session/changePin.dart';
|
||||
import '../session/settingUpPinAndBiometric.dart';
|
||||
import '../verify.dart';
|
||||
|
||||
class AppRouter {
|
||||
// static final SessionNotifier sessionNotifier = SessionNotifier();
|
||||
static GoRouter createRouter() {
|
||||
return GoRouter(
|
||||
// On web, respect the browser URL if present; otherwise fall back to login.
|
||||
initialLocation: kIsWeb ? '/login' : '/splash',
|
||||
debugLogDiagnostics: true,
|
||||
redirect: (BuildContext context, GoRouterState state) async {
|
||||
const allowedWithoutToken = [
|
||||
'/login',
|
||||
'/verify',
|
||||
'/mailVerify',
|
||||
'/pinPage',
|
||||
'/splash',
|
||||
];
|
||||
|
||||
// Always work with the matched path (ignores query params).
|
||||
final location = state.matchedLocation;
|
||||
final hasToken = await TokenService.hasValidToken();
|
||||
|
||||
final isGuestRoute = allowedWithoutToken.contains(location);
|
||||
|
||||
if (hasToken && isGuestRoute) {
|
||||
// If a logged-in user tries to access a guest route, redirect to home
|
||||
return '/home';
|
||||
}
|
||||
|
||||
if (!hasToken && !isGuestRoute) {
|
||||
// If a guest user tries to access a protected route, redirect to login
|
||||
return '/login';
|
||||
}
|
||||
|
||||
return null; // No redirect needed
|
||||
},
|
||||
routes: [
|
||||
if (!kIsWeb)
|
||||
GoRoute(
|
||||
path: '/splash',
|
||||
builder: (context, state) => const SplashScreen(),
|
||||
),
|
||||
GoRoute(
|
||||
path: '/login',
|
||||
builder: (context, state) => const login(),
|
||||
),
|
||||
GoRoute(
|
||||
path: '/mailVerify',
|
||||
builder: (context, state) {
|
||||
final data = state.extra as Map<String, dynamic>;
|
||||
final type = data['type'] as String;
|
||||
final value = data['value'] as String;
|
||||
return MyEmailVerify(type: type, value: value);
|
||||
},
|
||||
),
|
||||
// Legacy Firebase-phone OTP route is commented out in verify.dart,
|
||||
// so we do not expose /verify from GoRouter anymore.
|
||||
GoRoute(
|
||||
path: '/home',
|
||||
builder: (context, state) => Home(),
|
||||
),
|
||||
GoRoute(
|
||||
path: '/pinSettingPage',
|
||||
builder: (context, state) => pinSettingPage(),
|
||||
),
|
||||
GoRoute(
|
||||
path: '/pinPage',
|
||||
builder: (context, state) => pinPage(),
|
||||
),
|
||||
GoRoute(
|
||||
path: '/changePin',
|
||||
builder: (context, state) => changePin(),
|
||||
),
|
||||
GoRoute(
|
||||
path: '/claimprocess',
|
||||
builder: (context, state) => claimprocess(),
|
||||
),
|
||||
GoRoute(
|
||||
path: '/policies',
|
||||
builder: (context, state) {
|
||||
final arguments =
|
||||
state.extra as Map<String, dynamic>?; // optional args
|
||||
return policies(arguments: arguments);
|
||||
},
|
||||
),
|
||||
GoRoute(
|
||||
path: '/claims',
|
||||
builder: (context, state) {
|
||||
final int tabIndex = state.extra as int? ?? 0;
|
||||
return claims(initialTab: tabIndex);
|
||||
},
|
||||
),
|
||||
GoRoute(
|
||||
path: '/profile',
|
||||
builder: (context, state) => profile(),
|
||||
),
|
||||
GoRoute(
|
||||
path: '/help',
|
||||
builder: (context, state) => help(),
|
||||
),
|
||||
GoRoute(
|
||||
path: '/wellness',
|
||||
builder: (context, state) => wellness(),
|
||||
),
|
||||
GoRoute(
|
||||
path: '/privacypolicy',
|
||||
builder: (context, state) => privacypolicy(),
|
||||
),
|
||||
GoRoute(
|
||||
path: '/termsofuse',
|
||||
builder: (context, state) => termsofuse(),
|
||||
),
|
||||
GoRoute(
|
||||
path: '/generalExclusionsDeductibles',
|
||||
builder: (context, state) => generalExclusionsDeductibles(),
|
||||
),
|
||||
GoRoute(
|
||||
path: '/planclaimsform',
|
||||
builder: (context, state) {
|
||||
final details = state.extra as Map<String, dynamic>?;
|
||||
return planclaimsform(details: details);
|
||||
},
|
||||
),
|
||||
GoRoute(
|
||||
path: '/retailClaimForm',
|
||||
builder: (context, state) {
|
||||
final details = state.extra as Map<String, dynamic>?;
|
||||
return retailClaimForm(details: details);
|
||||
},
|
||||
),
|
||||
GoRoute(
|
||||
path: '/raisedTicketHistory',
|
||||
builder: (context, state) => raisedTicketHistory(),
|
||||
),
|
||||
GoRoute(
|
||||
path: '/tickettracklist/:ticketID',
|
||||
builder: (context, state) {
|
||||
final ticketID = state.pathParameters['ticketID']!;
|
||||
return tickettracklist(ticketID: ticketID);
|
||||
},
|
||||
),
|
||||
GoRoute(
|
||||
path: '/empDetails',
|
||||
builder: (context, state) => empDetails(),
|
||||
),
|
||||
GoRoute(
|
||||
path: '/faqs',
|
||||
builder: (context, state) => faqs(),
|
||||
),
|
||||
GoRoute(
|
||||
path: '/addOnsDetails',
|
||||
builder: (context, state) => addOnsDetails(),
|
||||
),
|
||||
GoRoute(
|
||||
path: '/empReviewDetails',
|
||||
builder: (context, state) => empReviewDetails(),
|
||||
),
|
||||
GoRoute(
|
||||
path: '/tickets',
|
||||
builder: (context, state) => tickets(),
|
||||
),
|
||||
GoRoute(
|
||||
path: '/chatbot',
|
||||
builder: (context, state) => chatbot(),
|
||||
),
|
||||
GoRoute(
|
||||
path: '/wellnessWebView',
|
||||
builder: (context, state) {
|
||||
final url = state.extra as String;
|
||||
return WellnessWebView(url: url);
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
//
|
||||
// //
|
||||
// // class AppRouter {
|
||||
|
||||
@ -511,7 +511,8 @@ class _MyEmailVerifyState extends State<MyEmailVerify> {
|
||||
session.empClientBranchId == null || session.empClientBranchId!.isEmpty) {
|
||||
prefs.setBool('isRetailLoggedIn', true);
|
||||
}
|
||||
context.go('/home');
|
||||
// context.go('/home');
|
||||
context.replace('/home');
|
||||
}
|
||||
// if (emp_status == 'enrolled' || emp_status == 'active') {
|
||||
// context.go('/home');
|
||||
@ -686,10 +687,12 @@ class _MyEmailVerifyState extends State<MyEmailVerify> {
|
||||
|
||||
if (_postToken != null && _postToken.isNotEmpty) {
|
||||
ToastHelper.showSuccessToast(context, 'Successfully Logged In');
|
||||
context.go('/home');
|
||||
context.replace('/home');
|
||||
// context.go('/home');
|
||||
// Navigator.pushReplacementNamed(context, 'home');
|
||||
} else {
|
||||
context.go('/empDetails');
|
||||
context.replace('/empDetails');
|
||||
// context.go('/empDetails');
|
||||
// Navigator.pushReplacementNamed(context, 'empDetails');
|
||||
}
|
||||
|
||||
@ -740,10 +743,12 @@ class _MyEmailVerifyState extends State<MyEmailVerify> {
|
||||
if (response.statusCode == 200) {
|
||||
Map<String, dynamic> data = json.decode(response.body);
|
||||
if(data['status'] == 'success') {
|
||||
context.go('/${route}');
|
||||
context.replace('/${route}');
|
||||
// context.go('/${route}');
|
||||
} else if(data['status'] == 'failed'){
|
||||
print('setPassword');
|
||||
context.goNamed(
|
||||
// goNamed
|
||||
context.replaceNamed(
|
||||
'setPassword',
|
||||
queryParameters: {
|
||||
'email': email_id,
|
||||
@ -930,7 +935,7 @@ class _MyEmailVerifyState extends State<MyEmailVerify> {
|
||||
),
|
||||
Container(
|
||||
margin: marginInsets,
|
||||
alignment: Alignment.bottomCenter,
|
||||
alignment: Alignment.center,
|
||||
child: SingleChildScrollView(
|
||||
child: Form(
|
||||
key: _formKey,
|
||||
@ -1272,14 +1277,14 @@ class _MyEmailVerifyState extends State<MyEmailVerify> {
|
||||
// ),
|
||||
SizedBox(
|
||||
height: Responsive.isDesktop(context)
|
||||
? _size.height * 0.3
|
||||
: _size.height * 0.2,
|
||||
? _size.height * 0.2
|
||||
: _size.height * 0.15,
|
||||
),
|
||||
// SizedBox(
|
||||
// height: _size.height * 0.1,
|
||||
// ),
|
||||
Container(
|
||||
alignment: Alignment.bottomCenter,
|
||||
alignment: Alignment.center,
|
||||
padding:
|
||||
EdgeInsets.symmetric(vertical: 8),
|
||||
child: RichText(
|
||||
|
||||
@ -1428,15 +1428,14 @@ class _addOnsDetailsState extends State<addOnsDetails> {
|
||||
|
||||
Future<void> backFunction() async {
|
||||
final SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||
if (prefs.containsKey('hrtoken')) {
|
||||
String? mobileNo = prefs.getString('fromHrLoginMobileNo');
|
||||
Navigator.pushNamed(context, 'empDetails',
|
||||
arguments: {'mobile': mobileNo});
|
||||
} else {
|
||||
context.go('/empDetails');
|
||||
// Navigator.pushNamed(context, 'empDetails');
|
||||
}
|
||||
}
|
||||
if (prefs.containsKey('hrtoken')) {
|
||||
String? mobileNo = prefs.getString('fromHrLoginMobileNo');
|
||||
Navigator.pushNamed(context, 'empDetails',
|
||||
arguments: {'mobile': mobileNo});
|
||||
} else {
|
||||
context.pop();
|
||||
// Navigator.pushNamed(context, 'empDetails');
|
||||
} }
|
||||
|
||||
void checkSiTopUp() async {
|
||||
List<Map<String, dynamic>> siData = [
|
||||
@ -1936,6 +1935,14 @@ class _addOnsDetailsState extends State<addOnsDetails> {
|
||||
Image.network(
|
||||
clientLogo ?? '', // Nhance logo
|
||||
height: 40,
|
||||
errorBuilder: (BuildContext context, Object error,
|
||||
StackTrace? stackTrace) {
|
||||
return Image.asset(
|
||||
'assets/Solid_gray.png',
|
||||
height: 40,
|
||||
fit: BoxFit.contain,
|
||||
);
|
||||
},
|
||||
),
|
||||
// Image.network(
|
||||
// 'https://i.imgur.com/qOihOvk.png', // Prodapt logo
|
||||
@ -4520,8 +4527,7 @@ class _addOnsDetailsState extends State<addOnsDetails> {
|
||||
checkDependentTopUp();
|
||||
sendAddonsGmcDependentToAPI();
|
||||
}
|
||||
context.go('/empReviewDetails');
|
||||
// Navigator.pushNamed(context,
|
||||
context.push('/empReviewDetails'); // Navigator.pushNamed(context,
|
||||
// 'empReviewDetails');
|
||||
}
|
||||
: null,
|
||||
|
||||
@ -760,15 +760,15 @@ class _empDetailsState extends State<empDetails> {
|
||||
),
|
||||
);
|
||||
},
|
||||
// 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: 80,
|
||||
// fit: BoxFit.cover,
|
||||
// );
|
||||
// },
|
||||
errorBuilder: (BuildContext context,
|
||||
Object error, StackTrace? stackTrace) {
|
||||
return Image.asset(
|
||||
'assets/Solid_gray.png',
|
||||
width: 80,
|
||||
height: 80,
|
||||
fit: BoxFit.cover,
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
@ -976,7 +976,7 @@ class _empDetailsState extends State<empDetails> {
|
||||
alignment: Alignment.centerRight,
|
||||
child: ElevatedButton(
|
||||
onPressed: () {
|
||||
context.go('/addOnsDetails');
|
||||
context.push('/addOnsDetails');
|
||||
// Navigator.pushNamed(
|
||||
// context, 'addOnsDetails');
|
||||
},
|
||||
@ -2240,6 +2240,8 @@ class _empDetailsState extends State<empDetails> {
|
||||
print('gmcFloaterTextDescription $gmcFloaterTextDescription');
|
||||
String gmcNotes = item['notes'];
|
||||
print('gmcNotes $gmcNotes');
|
||||
String cleanedNotes = gmcNotes?.toString().toLowerCase().replaceAll(' ', '') ?? '';
|
||||
print('gmcNotes cleaned: $cleanedNotes');
|
||||
dynamic gmcECardDownload = item['eCardDownload'];
|
||||
print('gmcECardDownload $gmcECardDownload');
|
||||
bool gmcCopyDependenceDataEnable = item['copy_dependence_data_enable'];
|
||||
@ -2635,46 +2637,134 @@ class _empDetailsState extends State<empDetails> {
|
||||
Column(
|
||||
children: familyFloaterContainers,
|
||||
),
|
||||
// Add Family Member Button HERE
|
||||
if (gmcOpenForEnrollment != 0 && gmcECardDownload == null)...[
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
if (getFalseObjects.length == 0) {
|
||||
ToastHelper.showWarningToast(context, "No family member to add");
|
||||
return;
|
||||
}
|
||||
openAddFamilyMemberPopup(
|
||||
"Add",
|
||||
null,
|
||||
gmcClientPolicyId,
|
||||
gmcRelationShip,
|
||||
gmcSumInsured,
|
||||
);
|
||||
},
|
||||
child: Container(
|
||||
margin: EdgeInsets.only(top: 10),
|
||||
padding: EdgeInsets.all(15),
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(color: Colors.black, width: 1),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Icon(Icons.person_add, color: Colors.black),
|
||||
SizedBox(width: 10),
|
||||
Text(
|
||||
"Add Family Member",
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: Responsive.isDesktop(context) ? 20 : 16,
|
||||
fontWeight: FontWeight.w500,
|
||||
// Add Family Member Button:
|
||||
// Show only when:
|
||||
// - enrollment is open
|
||||
// - e-card not generated
|
||||
// - there are still dependants left to add
|
||||
// - and the policy allows relationships beyond just "Self"
|
||||
// if (gmcOpenForEnrollment != 0 &&
|
||||
// gmcECardDownload == null &&
|
||||
// getFalseObjects.isNotEmpty &&
|
||||
// gmcRelationShip.any((rel) =>
|
||||
// (rel?.toString().toLowerCase() ?? '') != 'self')) ...[
|
||||
// Builder(builder: (context) {
|
||||
// print("*** Condition is TRUE");
|
||||
// GestureDetector(
|
||||
// onTap: () {
|
||||
// if (getFalseObjects.length == 0) {
|
||||
// ToastHelper.showWarningToast(context, "No family member to add");
|
||||
// return;
|
||||
// }
|
||||
// openAddFamilyMemberPopup(
|
||||
// "Add",
|
||||
// null,
|
||||
// gmcClientPolicyId,
|
||||
// gmcRelationShip,
|
||||
// gmcSumInsured,
|
||||
// );
|
||||
// },
|
||||
// child: Container(
|
||||
// margin: EdgeInsets.only(top: 10),
|
||||
// padding: EdgeInsets.all(15),
|
||||
// decoration: BoxDecoration(
|
||||
// border: Border.all(color: Colors.black, width: 1),
|
||||
// borderRadius: BorderRadius.circular(8),
|
||||
// ),
|
||||
// child: Row(
|
||||
// mainAxisAlignment: MainAxisAlignment.center,
|
||||
// children: [
|
||||
// Icon(Icons.person_add, color: Colors.black),
|
||||
// SizedBox(width: 10),
|
||||
// Text(
|
||||
// "Add Family Member",
|
||||
// style: GoogleFonts.poppins(
|
||||
// fontSize: Responsive.isDesktop(context) ? 20 : 16,
|
||||
// fontWeight: FontWeight.w500,
|
||||
// ),
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ],
|
||||
if (cleanedNotes != 'allowedmembersself') ...<Widget>[
|
||||
if (gmcOpenForEnrollment != 0 &&
|
||||
gmcECardDownload == null &&
|
||||
getFalseObjects.isNotEmpty &&
|
||||
gmcRelationShip.any((rel) =>
|
||||
(rel is Map
|
||||
? rel['relationship']?.toString().toLowerCase()
|
||||
: rel?.toString().toLowerCase() ?? '') != 'self')) ...<Widget>[
|
||||
Builder(builder: (context) {
|
||||
print("*** IF - Condition is TRUE");
|
||||
print("*** gmcOpenForEnrollment: $gmcOpenForEnrollment");
|
||||
print("*** gmcECardDownload: $gmcECardDownload");
|
||||
print("*** getFalseObjects length: ${getFalseObjects.length}");
|
||||
print("*** gmcRelationShip full: $gmcRelationShip");
|
||||
print("*** gmcNotes original: $gmcNotes");
|
||||
print("*** gmcNotes cleaned: $cleanedNotes");
|
||||
|
||||
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
if (getFalseObjects.length == 0) {
|
||||
ToastHelper.showWarningToast(context, "No family member to add");
|
||||
return;
|
||||
}
|
||||
openAddFamilyMemberPopup(
|
||||
"Add",
|
||||
null,
|
||||
gmcClientPolicyId,
|
||||
gmcRelationShip,
|
||||
gmcSumInsured,
|
||||
);
|
||||
},
|
||||
child: Container(
|
||||
margin: EdgeInsets.only(top: 10),
|
||||
padding: EdgeInsets.all(15),
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(color: Colors.black, width: 1),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Icon(Icons.person_add, color: Colors.black),
|
||||
SizedBox(width: 10),
|
||||
Text(
|
||||
"Add Family Member",
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: Responsive.isDesktop(context) ? 20 : 16,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}),
|
||||
] else ...<Widget>[
|
||||
Builder(builder: (context) {
|
||||
print("*** ELSE - Condition is FALSE");
|
||||
print("*** gmcOpenForEnrollment: $gmcOpenForEnrollment → pass: ${gmcOpenForEnrollment != 0}");
|
||||
print("*** gmcECardDownload: $gmcECardDownload → pass: ${gmcECardDownload == null}");
|
||||
print("*** getFalseObjects length: ${getFalseObjects.length} → pass: ${getFalseObjects.isNotEmpty}");
|
||||
print("*** gmcRelationShip full list: $gmcRelationShip");
|
||||
print("*** gmcNotes original: $gmcNotes");
|
||||
print("*** gmcNotes cleaned: $cleanedNotes");
|
||||
return SizedBox.shrink(); // no widget shown in else
|
||||
}),
|
||||
],
|
||||
]else ...<Widget>[
|
||||
Builder(builder: (context) {
|
||||
print("*** OUTER ELSE - Condition is FALSE");
|
||||
print("*** gmcNotes original: $gmcNotes");
|
||||
print("*** gmcNotes cleaned: $cleanedNotes");
|
||||
return SizedBox.shrink();
|
||||
}),
|
||||
|
||||
],
|
||||
SizedBox(height: 15),
|
||||
if (Responsive.isDesktop(context) && gmcIsValueValid)
|
||||
Row(
|
||||
|
||||
@ -1413,6 +1413,14 @@ setState(() {
|
||||
Image.network(
|
||||
clientLogo ?? '', // Nhance logo
|
||||
height: 40,
|
||||
errorBuilder: (BuildContext context, Object error,
|
||||
StackTrace? stackTrace) {
|
||||
return Image.asset(
|
||||
'assets/Solid_gray.png',
|
||||
height: 40,
|
||||
fit: BoxFit.contain,
|
||||
);
|
||||
},
|
||||
),
|
||||
// Image.network(
|
||||
// 'https://i.imgur.com/qOihOvk.png', // Prodapt logo
|
||||
|
||||
@ -278,7 +278,7 @@ class _loginState extends State<login> {
|
||||
print('isEmailFieldVisible $isEmailFieldVisible');
|
||||
// prefs.setString('empEmail', emailController.text);
|
||||
print('${emailMobileController.text}');
|
||||
context.push(
|
||||
context.go(
|
||||
'/mailVerify',
|
||||
extra: {
|
||||
'type': 'email',
|
||||
@ -287,7 +287,7 @@ class _loginState extends State<login> {
|
||||
);
|
||||
} else {
|
||||
// _verifyPhoneNumber();
|
||||
context.push(
|
||||
context.go(
|
||||
'/mailVerify',
|
||||
extra: {
|
||||
'type': 'mobile',
|
||||
@ -690,6 +690,14 @@ class _loginState extends State<login> {
|
||||
clickedForgotPassword = true;
|
||||
passwordController.text = '';
|
||||
passwordController.clear();
|
||||
|
||||
// ✅ Clear everything so the flow starts fresh
|
||||
emailController.clear();
|
||||
_otpController.clear();
|
||||
resetPasswordController.clear();
|
||||
confirmPasswordController.clear();
|
||||
|
||||
_formKey.currentState?.reset();
|
||||
});
|
||||
}
|
||||
|
||||
@ -800,8 +808,17 @@ class _loginState extends State<login> {
|
||||
otpValueStatus = false;
|
||||
otpFieldShow = false;
|
||||
clickedForgotPassword = false;
|
||||
resetPasswordEnable = true;
|
||||
resetPasswordEnable = true; // This shows the reset fields
|
||||
_isLoading = false;
|
||||
hasMinLength = false;
|
||||
hasUpperLower = false;
|
||||
hasNumber = false;
|
||||
hasSpecialChar = false;
|
||||
|
||||
// ✅ ADD THESE LINES TO CLEAR CACHED DATA
|
||||
resetPasswordController.clear();
|
||||
confirmPasswordController.clear();
|
||||
_formKey.currentState?.reset();
|
||||
});
|
||||
// ToastHelper.showSuccessToast(context, message);
|
||||
} else {
|
||||
@ -871,6 +888,17 @@ class _loginState extends State<login> {
|
||||
resetPasswordEnable = false;
|
||||
clickedForgotPassword = false;
|
||||
otpFieldShow = false;
|
||||
|
||||
// ✅ CLEAR THE CONTROLLERS HERE
|
||||
resetPasswordController.clear();
|
||||
confirmPasswordController.clear();
|
||||
_otpController.clear();
|
||||
// emailController.clear(); // Uncomment if you want the email cleared too
|
||||
|
||||
// Reset the form state to clear validation error messages
|
||||
_formKey.currentState?.reset();
|
||||
emailController.text = '';
|
||||
emailController.clear();
|
||||
});
|
||||
// ToastHelper.showSuccessToast(context, message);
|
||||
} else {
|
||||
@ -1095,7 +1123,7 @@ class _loginState extends State<login> {
|
||||
child: Image.asset(
|
||||
'assets/nhance_app_logo.png',
|
||||
width: 150,
|
||||
height: 100,
|
||||
height: 150,
|
||||
)),
|
||||
),
|
||||
],
|
||||
@ -1109,7 +1137,7 @@ class _loginState extends State<login> {
|
||||
),
|
||||
Container(
|
||||
margin: marginInsets,
|
||||
alignment: Alignment.bottomCenter,
|
||||
alignment: Alignment.center,
|
||||
child: SingleChildScrollView(
|
||||
child: Form(
|
||||
key: _formKey,
|
||||
@ -1120,9 +1148,7 @@ class _loginState extends State<login> {
|
||||
Expanded(
|
||||
flex: _size.width < 1100 ? 6 : 12,
|
||||
child: Container(
|
||||
margin: _size.width > 1100
|
||||
? EdgeInsets.only(left: 20, right: 20)
|
||||
: EdgeInsets.only(left: 0, right: 0),
|
||||
margin: EdgeInsets.symmetric(horizontal: 20),
|
||||
child: Column(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.center,
|
||||
@ -1638,152 +1664,114 @@ class _loginState extends State<login> {
|
||||
},
|
||||
),
|
||||
),
|
||||
if (otpFieldShow) ...[
|
||||
SizedBox(height: 10),
|
||||
AnimatedSwitcher(
|
||||
duration: const Duration(
|
||||
milliseconds: 500,
|
||||
), // animation speed
|
||||
switchInCurve:
|
||||
Curves.easeInOutCirc,
|
||||
switchOutCurve:
|
||||
Curves.easeOutCirc,
|
||||
child:
|
||||
clickedForgotPassword
|
||||
? Column(
|
||||
key: const ValueKey(
|
||||
'otp_block'),
|
||||
crossAxisAlignment:
|
||||
CrossAxisAlignment
|
||||
.start,
|
||||
children: [
|
||||
Container(
|
||||
child: Center(
|
||||
child:
|
||||
Text(
|
||||
'OTP',
|
||||
style:
|
||||
TextStyle(
|
||||
fontSize: Responsive.isMobile(context)
|
||||
? 14
|
||||
: 18,
|
||||
fontWeight:
|
||||
FontWeight.w600,
|
||||
color:
|
||||
if (otpFieldShow &&
|
||||
clickedForgotPassword) ...[
|
||||
const SizedBox(height: 10),
|
||||
Column(
|
||||
crossAxisAlignment:
|
||||
CrossAxisAlignment.start,
|
||||
children: [
|
||||
Center(
|
||||
child: Text(
|
||||
'OTP',
|
||||
style: TextStyle(
|
||||
fontSize: Responsive
|
||||
.isMobile(
|
||||
context)
|
||||
? 14
|
||||
: 18,
|
||||
fontWeight:
|
||||
FontWeight
|
||||
.w600,
|
||||
color:
|
||||
Colors.black,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 10),
|
||||
Container(
|
||||
alignment:
|
||||
Alignment.center,
|
||||
margin: Responsive
|
||||
.isDesktop(
|
||||
context)
|
||||
? const EdgeInsets
|
||||
.symmetric(
|
||||
horizontal:
|
||||
150)
|
||||
: const EdgeInsets
|
||||
.symmetric(
|
||||
horizontal:
|
||||
0),
|
||||
child: Pinput(
|
||||
length: 6,
|
||||
inputFormatters: [
|
||||
FilteringTextInputFormatter
|
||||
.digitsOnly,
|
||||
],
|
||||
keyboardType:
|
||||
TextInputType
|
||||
.number,
|
||||
showCursor: true,
|
||||
controller:
|
||||
_otpController,
|
||||
validator:
|
||||
(value) {
|
||||
if (value ==
|
||||
null ||
|
||||
value
|
||||
.isEmpty) {
|
||||
return 'Please enter OTP';
|
||||
}
|
||||
if (value
|
||||
.length <
|
||||
6) {
|
||||
return 'OTP must be 6 digits';
|
||||
}
|
||||
return null;
|
||||
},
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 10),
|
||||
Container(
|
||||
margin: Responsive
|
||||
.isDesktop(
|
||||
context)
|
||||
? const EdgeInsets
|
||||
.symmetric(
|
||||
horizontal:
|
||||
150)
|
||||
: const EdgeInsets
|
||||
.symmetric(
|
||||
horizontal:
|
||||
0),
|
||||
alignment: Alignment
|
||||
.centerRight,
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
setState(() {
|
||||
otpFieldShow =
|
||||
false;
|
||||
});
|
||||
resendOTP();
|
||||
},
|
||||
mouseCursor:
|
||||
SystemMouseCursors
|
||||
.click,
|
||||
child: Text(
|
||||
'Didn’t Receive Code?',
|
||||
style: GoogleFonts
|
||||
.poppins(
|
||||
color: Colors
|
||||
.blue,
|
||||
fontSize: 14,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height:
|
||||
10),
|
||||
Container(
|
||||
alignment: Alignment.center,
|
||||
margin: Responsive.isDesktop(context)
|
||||
? EdgeInsets.symmetric(
|
||||
horizontal: 150)
|
||||
: EdgeInsets.symmetric(
|
||||
horizontal: 0),
|
||||
child: Pinput(
|
||||
length: 6,
|
||||
// defaultPinTheme: defaultPinTheme,
|
||||
// focusedPinTheme: focusedPinTheme,
|
||||
// submittedPinTheme: submittedPinTheme,
|
||||
inputFormatters: [
|
||||
FilteringTextInputFormatter
|
||||
.digitsOnly, // ✅ allows only 0–9
|
||||
],
|
||||
keyboardType: TextInputType.number,
|
||||
showCursor: true,
|
||||
controller: _otpController,
|
||||
validator:
|
||||
(value) {
|
||||
if (value == null ||
|
||||
value.isEmpty) {
|
||||
return 'Please enter OTP';
|
||||
}
|
||||
if (value.length <
|
||||
6) {
|
||||
return 'OTP must be 6 digits';
|
||||
}
|
||||
// if (otpValueStatus) {
|
||||
// // example
|
||||
// return 'Invalid OTP';
|
||||
// }
|
||||
return null;
|
||||
},
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height:
|
||||
10),
|
||||
Container(
|
||||
margin: Responsive.isDesktop(context)
|
||||
? EdgeInsets.symmetric(
|
||||
horizontal:
|
||||
150)
|
||||
: EdgeInsets.symmetric(
|
||||
horizontal:
|
||||
0),
|
||||
alignment:
|
||||
Alignment
|
||||
.centerRight, // center the text
|
||||
child:InkWell(
|
||||
onTap: (){
|
||||
setState(() {
|
||||
otpFieldShow = false;
|
||||
});
|
||||
print('ABCDEF');
|
||||
resendOTP();
|
||||
},
|
||||
mouseCursor: SystemMouseCursors.click,
|
||||
child: Text(
|
||||
'Didn’t Receive Code?',
|
||||
style: GoogleFonts.poppins(
|
||||
color: Colors.blue,
|
||||
fontSize: 14,
|
||||
),
|
||||
),
|
||||
),
|
||||
// RichText(
|
||||
// textAlign:
|
||||
// TextAlign.right,
|
||||
// text:
|
||||
// TextSpan(
|
||||
// text:
|
||||
// 'Didn’t Receive Code? ', // normal text
|
||||
// style:
|
||||
// TextStyle(
|
||||
// fontSize: Responsive.isMobile(context)
|
||||
// ? 12
|
||||
// : 14,
|
||||
// fontWeight:
|
||||
// FontWeight.normal,
|
||||
// color:
|
||||
// Colors.black,
|
||||
// ),
|
||||
// children: [
|
||||
// TextSpan(
|
||||
// text: 'Resend', // bold clickable part
|
||||
// style: TextStyle(
|
||||
// fontWeight: FontWeight.bold,
|
||||
// color: Colors.white,
|
||||
// decoration: TextDecoration.underline, // optional
|
||||
// ),
|
||||
// // recognizer: TapGestureRecognizer()
|
||||
// // ..onTap = () {
|
||||
// //
|
||||
// // },
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// ),
|
||||
),
|
||||
],
|
||||
)
|
||||
: const SizedBox
|
||||
.shrink(), // Empty widget when false
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
SizedBox(height: 20),
|
||||
@ -1856,6 +1844,7 @@ class _loginState extends State<login> {
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
child: TextFormField(
|
||||
key: const ValueKey('new_password_field'),
|
||||
controller: resetPasswordController,
|
||||
obscureText: _resetObscurePassword,
|
||||
onChanged: validatePassword,
|
||||
@ -1915,6 +1904,7 @@ class _loginState extends State<login> {
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
child: TextFormField(
|
||||
key: const ValueKey('confirm_password_field'),
|
||||
controller: confirmPasswordController,
|
||||
obscureText: _obscureConfirmPassword,
|
||||
textAlignVertical: TextAlignVertical.center,
|
||||
|
||||
@ -127,13 +127,15 @@ class _AddPolicyScreenState extends State<AddPolicyScreen> {
|
||||
policyNoController.clear();
|
||||
expDateController.clear();
|
||||
|
||||
setState(() => isLoading = false);
|
||||
|
||||
context.go('/home');
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
print('Submit error: $e');
|
||||
} finally {
|
||||
if (mounted) {
|
||||
setState(() => isLoading = false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -177,7 +177,8 @@ import '../service/popup_helper.dart';
|
||||
canPop: false,
|
||||
onPopInvokedWithResult: (didPop, result) {
|
||||
if (didPop) return;
|
||||
context.go('/claims');
|
||||
// context.go('/claims');
|
||||
context.pop();
|
||||
},
|
||||
child: Scaffold(
|
||||
backgroundColor: Colors.white,
|
||||
@ -209,7 +210,7 @@ import '../service/popup_helper.dart';
|
||||
flex: 12,
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
context.go('/claims');
|
||||
context.pop(); // context.go('/claims');
|
||||
},
|
||||
child: Row(
|
||||
mainAxisAlignment:
|
||||
|
||||
@ -311,11 +311,18 @@ class _claimsState extends State<claims> {
|
||||
if (widget.initialTab == 2) {
|
||||
context.go('/home');
|
||||
}
|
||||
if (widget.initialTab == 0) {
|
||||
context.go('/home');
|
||||
} else {
|
||||
context.pop();
|
||||
}
|
||||
|
||||
if (Navigator.canPop(context)) {
|
||||
context.pop();
|
||||
} else {
|
||||
context.go('/home');
|
||||
}
|
||||
|
||||
// if (widget.initialTab == 0) {
|
||||
// context.pop();
|
||||
// } else {
|
||||
// context.pop();
|
||||
// }
|
||||
},
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
@ -993,14 +1000,14 @@ class _claimsState extends State<claims> {
|
||||
var details = {
|
||||
"retailDetails": item,
|
||||
};
|
||||
context.go('/retailClaimForm', extra: details);
|
||||
context.push('/retailClaimForm', extra: details);
|
||||
} else {
|
||||
// Normal claim policy click
|
||||
var details = {
|
||||
"claimsDetails": item,
|
||||
"fromClaimPage": 0,
|
||||
};
|
||||
context.go('/planclaimsform', extra: details);
|
||||
context.push('/planclaimsform', extra: details);
|
||||
}
|
||||
},
|
||||
child: MouseRegion(
|
||||
|
||||
@ -192,11 +192,55 @@ class _generalExclusionsDeductiblesState
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'General Exclusions & Deductibles',
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 18, fontWeight: FontWeight.w600),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
Expanded(
|
||||
flex: 12,
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
context.go('/home');
|
||||
},
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
Icon(
|
||||
Icons.chevron_left,
|
||||
color: Color(0xFF000000),
|
||||
size: 30,
|
||||
),
|
||||
SizedBox(
|
||||
width: Responsive.isDesktop(context)
|
||||
? 0
|
||||
: 5),
|
||||
Column(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.center,
|
||||
crossAxisAlignment:
|
||||
CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'General Exclusions & Deductibles',
|
||||
textAlign: TextAlign.start,
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Color(0xFF000000),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
// Text(
|
||||
// 'General Exclusions & Deductibles',
|
||||
// style: GoogleFonts.poppins(
|
||||
// fontSize: 18, fontWeight: FontWeight.w600),
|
||||
// ),
|
||||
const SizedBox(height: 20),
|
||||
if (type3Content.isNotEmpty) ...[
|
||||
Text(type3SectionName ?? '',
|
||||
|
||||
@ -336,31 +336,31 @@ class _helpState extends State<help> {
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
Expanded(
|
||||
flex: 12,
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
context.push('/claims');
|
||||
// Navigator.pushNamed(context, 'home');
|
||||
},
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
Icon(
|
||||
Icons
|
||||
.chevron_left, // Replace with your desired icon
|
||||
color: Color(0xFF000000),
|
||||
size: 30,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
// Row(
|
||||
// mainAxisAlignment: MainAxisAlignment.start,
|
||||
// children: [
|
||||
// Expanded(
|
||||
// flex: 12,
|
||||
// child: InkWell(
|
||||
// onTap: () {
|
||||
// context.push('/claims');
|
||||
// // Navigator.pushNamed(context, 'home');
|
||||
// },
|
||||
// child: Row(
|
||||
// mainAxisAlignment: MainAxisAlignment.start,
|
||||
// children: [
|
||||
// Icon(
|
||||
// Icons
|
||||
// .chevron_left, // Replace with your desired icon
|
||||
// color: Color(0xFF000000),
|
||||
// size: 30,
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
|
||||
@ -1880,10 +1880,44 @@ class _HomeState extends State<Home> {
|
||||
cards.add(_buildAddCard()); // <-- always add this card
|
||||
}
|
||||
|
||||
// ✅ NEW: Show empty state when inactive tab has no policies
|
||||
if (!isActive && cards.isEmpty) {
|
||||
return _buildEmptyState("No inactive policies available");
|
||||
}
|
||||
|
||||
return _buildCarouselSlider(cards);
|
||||
}
|
||||
|
||||
Widget _buildEmptyState(String message) {
|
||||
return Container(
|
||||
width: double.infinity,
|
||||
margin: const EdgeInsets.symmetric(vertical: 8),
|
||||
padding: const EdgeInsets.symmetric(vertical: 40, horizontal: 20),
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFFF7F7F7),
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
border: Border.all(color: const Color(0xFFE0E0E0), width: 1.5),
|
||||
),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Icon(Icons.folder_off_outlined, size: 48, color: Colors.grey[400]),
|
||||
const SizedBox(height: 12),
|
||||
Text(
|
||||
message,
|
||||
textAlign: TextAlign.center,
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Colors.grey[600],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildCarouselSlider(List<Widget> cards) {
|
||||
final int totalCards = cards.length;
|
||||
final bool hasAddCard = isActive; // because you add AddCard only when isActive
|
||||
|
||||
@ -137,6 +137,7 @@ class _planclaimsformState extends State<planclaimsform> {
|
||||
bool isIntimationDateValid = true;
|
||||
bool isAdmitDateValid = true;
|
||||
bool isDischargeDateValid = true;
|
||||
bool showFileError = false;
|
||||
|
||||
final session = SessionManager();
|
||||
|
||||
@ -591,57 +592,70 @@ class _planclaimsformState extends State<planclaimsform> {
|
||||
}
|
||||
|
||||
Future<void> sendFormDataToApi() async {
|
||||
setState(() => isSubmitting = true); // 🔥 start loader
|
||||
setState(() {
|
||||
isServiceValid = serviceId != null;
|
||||
isPolicyValid = policyNumberId != null;
|
||||
isMemberValid = selectedMemberId != null;
|
||||
isSubjectValid = subjectController.text.trim().isNotEmpty;
|
||||
isHospitalNameValid = (policyTypeCondition != 1 && policyTypeCondition != 72) || hospitalNameController.text.trim().isNotEmpty;
|
||||
isHospitalAddressValid = (policyTypeCondition != 1 && policyTypeCondition != 72) || hospitalAddressController.text.trim().isNotEmpty;
|
||||
isHospitalStateValid = (policyTypeCondition != 1 && policyTypeCondition != 72) || hospitalStateController.text.trim().isNotEmpty;
|
||||
isHospitalCityValid = (policyTypeCondition != 1 && policyTypeCondition != 72) || hospitalCityController.text.trim().isNotEmpty;
|
||||
isHospitalPincodeValid = (policyTypeCondition != 1 && policyTypeCondition != 72) || hospitalPinCodeController.text.trim().isNotEmpty;
|
||||
isHospitalPhoneNoValid = (policyTypeCondition != 1 && policyTypeCondition != 72) || hospitalPhoneNoController.text.trim().isNotEmpty;
|
||||
// isAdmitDischargeValid = policyTypeCondition != 1 || (admitDate != null && dischargeDate != null);
|
||||
isClaimTypeValid = (policyTypeCondition != 1 && policyTypeCondition != 72) || claimTypeId != null;
|
||||
isAdmitDateValid = (policyTypeCondition != 1 && policyTypeCondition != 72) || admitDate != null;
|
||||
isDischargeDateValid = (policyTypeCondition != 1 && policyTypeCondition != 72) || dischargeDate != null;
|
||||
isClaimAmountValid = serviceId != 1 || claimAmountController.text.trim().isNotEmpty;
|
||||
isAccidentDateValid = (serviceId == 2 || serviceId == 3 || serviceId == 4) ? accidentDate != null : true;
|
||||
isIntimationDateValid = (serviceId == 2 || serviceId == 3 || serviceId == 4) ? intimationDate != null : true;
|
||||
});
|
||||
final isServiceValid = serviceId != null;
|
||||
final isPolicyValid = policyNumberId != null;
|
||||
final isMemberValid = selectedMemberId != null;
|
||||
final isSubjectValid = subjectController.text.trim().isNotEmpty;
|
||||
final isHospitalNameValid = (policyTypeCondition != 1 && policyTypeCondition != 72) || hospitalNameController.text.trim().isNotEmpty;
|
||||
final isHospitalAddressValid = (policyTypeCondition != 1 && policyTypeCondition != 72) || hospitalAddressController.text.trim().isNotEmpty;
|
||||
final isHospitalStateValid = (policyTypeCondition != 1 && policyTypeCondition != 72) || hospitalStateController.text.trim().isNotEmpty;
|
||||
final isHospitalCityValid = (policyTypeCondition != 1 && policyTypeCondition != 72) || hospitalCityController.text.trim().isNotEmpty;
|
||||
final isHospitalPincodeValid = (policyTypeCondition != 1 && policyTypeCondition != 72) || hospitalPinCodeController.text.trim().isNotEmpty;
|
||||
final isHospitalPhoneNoValid = (policyTypeCondition != 1 && policyTypeCondition != 72) || (hospitalPhoneNoController.text.trim().length == 10);
|
||||
final isAdmitDateValid = (policyTypeCondition != 1 && policyTypeCondition != 72) || admitDate != null;
|
||||
final isDischargeDateValid = (policyTypeCondition != 1 && policyTypeCondition != 72) || dischargeDate != null;
|
||||
final isClaimAmountValid = serviceId != 1 || claimAmountController.text.trim().isNotEmpty;
|
||||
final isAccidentDateValid = (serviceId == 2 || serviceId == 3 || serviceId == 4) ? accidentDate != null : true;
|
||||
final isIntimationDateValid = (serviceId == 2 || serviceId == 3 || serviceId == 4) ? intimationDate != null : true;
|
||||
final areFilesUploaded = FileUploadService().files.isNotEmpty;
|
||||
|
||||
if (isServiceValid &&
|
||||
isClaimTypeValid &&
|
||||
isPolicyValid &&
|
||||
isMemberValid &&
|
||||
isSubjectValid &&
|
||||
isHospitalNameValid &&
|
||||
isHospitalAddressValid &&
|
||||
isHospitalStateValid &&
|
||||
isHospitalCityValid &&
|
||||
isHospitalPincodeValid &&
|
||||
isHospitalPhoneNoValid &&
|
||||
isAdmitDateValid &&
|
||||
isDischargeDateValid &&
|
||||
isClaimAmountValid &&
|
||||
isAccidentDateValid &&
|
||||
isIntimationDateValid) {
|
||||
if (FileUploadService().files.isEmpty) {
|
||||
setState(() {
|
||||
MultiFileUploadWidget.hasFiles = false;
|
||||
});
|
||||
ToastHelper.showErrorToast(context, 'Please upload at least one document');
|
||||
return;
|
||||
}
|
||||
// Proceed to submit
|
||||
} else {
|
||||
if (!isServiceValid ||
|
||||
!isPolicyValid ||
|
||||
!isMemberValid ||
|
||||
!isSubjectValid ||
|
||||
!isHospitalNameValid ||
|
||||
!isHospitalAddressValid ||
|
||||
!isHospitalStateValid ||
|
||||
!isHospitalCityValid ||
|
||||
!isHospitalPincodeValid ||
|
||||
!isHospitalPhoneNoValid ||
|
||||
!isAdmitDateValid ||
|
||||
!isDischargeDateValid ||
|
||||
!isClaimAmountValid ||
|
||||
!isAccidentDateValid ||
|
||||
!isIntimationDateValid) {
|
||||
setState(() {
|
||||
this.isServiceValid = isServiceValid;
|
||||
this.isPolicyValid = isPolicyValid;
|
||||
this.isMemberValid = isMemberValid;
|
||||
this.isSubjectValid = isSubjectValid;
|
||||
this.isHospitalNameValid = isHospitalNameValid;
|
||||
this.isHospitalAddressValid = isHospitalAddressValid;
|
||||
this.isHospitalStateValid = isHospitalStateValid;
|
||||
this.isHospitalCityValid = isHospitalCityValid;
|
||||
this.isHospitalPincodeValid = isHospitalPincodeValid;
|
||||
this.isHospitalPhoneNoValid = isHospitalPhoneNoValid;
|
||||
this.isAdmitDateValid = isAdmitDateValid;
|
||||
this.isDischargeDateValid = isDischargeDateValid;
|
||||
this.isClaimAmountValid = isClaimAmountValid;
|
||||
this.isAccidentDateValid = isAccidentDateValid;
|
||||
this.isIntimationDateValid = isIntimationDateValid;
|
||||
showFileError = true; // Also show file error if other fields are invalid
|
||||
});
|
||||
ToastHelper.showErrorToast(context, 'Please Fill Required Fields');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!areFilesUploaded) {
|
||||
setState(() {
|
||||
showFileError = true;
|
||||
});
|
||||
ToastHelper.showErrorToast(context, 'Please upload the document');
|
||||
return;
|
||||
}
|
||||
|
||||
setState(() {
|
||||
isSubmitting = true;
|
||||
isLoading = true;
|
||||
});
|
||||
|
||||
@ -711,7 +725,8 @@ class _planclaimsformState extends State<planclaimsform> {
|
||||
|
||||
_token = await TokenService.getPostToken();
|
||||
|
||||
final request = http.MultipartRequest('POST', Uri.parse('${Environment.apiUrl}initiateClaim'));
|
||||
final request = http.MultipartRequest(
|
||||
'POST', Uri.parse('${Environment.apiUrl}initiateClaim'));
|
||||
request.headers['Authorization'] = 'Bearer $_token';
|
||||
request.headers['APP-SIGNATURE'] = 'nhance-2025-signature-T8f6gV9k5fK2wf5V5c4vId3b0J8z6cAm2x8Y';
|
||||
final stringFields = fields.map((key, value) => MapEntry(key, value?.toString() ?? ''));
|
||||
@ -728,7 +743,10 @@ class _planclaimsformState extends State<planclaimsform> {
|
||||
print("📝 Checking label for file: ${uf.file.name}, current label: '${uf.label}'");
|
||||
if ((uf.label ?? '').trim().isEmpty) {
|
||||
ToastHelper.showErrorToast(context, 'Please enter name for all uploaded documents');
|
||||
setState(() => isLoading = false);
|
||||
setState(() {
|
||||
isLoading = false;
|
||||
isSubmitting = false; // ✅ reset loader
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -769,33 +787,20 @@ class _planclaimsformState extends State<planclaimsform> {
|
||||
// ✅ Convert image → PDF
|
||||
final pdf = pw.Document();
|
||||
final image = pw.MemoryImage(fileBytes);
|
||||
|
||||
pdf.addPage(
|
||||
pw.Page(
|
||||
build: (pw.Context context) => pw.Center(
|
||||
child: pw.Image(image, fit: pw.BoxFit.contain),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
fileBytes = await pdf.save(); // converted PDF bytes
|
||||
|
||||
// replace file name with .pdf extension
|
||||
pdf.addPage(pw.Page(
|
||||
build: (pw.Context context) =>
|
||||
pw.Center(child: pw.Image(image, fit: pw.BoxFit.contain)),
|
||||
));
|
||||
fileBytes = await pdf.save();
|
||||
final pdfFileName = pf.name.replaceAll(RegExp(r'\.\w+$'), '.pdf');
|
||||
|
||||
print('📄 Converted image ${pf.name} → PDF ($pdfFileName)');
|
||||
request.files.add(http.MultipartFile.fromBytes(
|
||||
'claim_docs[]',
|
||||
fileBytes,
|
||||
filename: pdfFileName,
|
||||
));
|
||||
'claim_docs[]', fileBytes, filename: pdfFileName));
|
||||
} else {
|
||||
// ✅ Already a PDF
|
||||
request.files.add(http.MultipartFile.fromBytes(
|
||||
'claim_docs[]',
|
||||
fileBytes,
|
||||
filename: pf.name,
|
||||
));
|
||||
'claim_docs[]', fileBytes, filename: pf.name));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -816,31 +821,25 @@ class _planclaimsformState extends State<planclaimsform> {
|
||||
final responseBody = await response.stream.bytesToString();
|
||||
|
||||
final decoded = jsonDecode(responseBody);
|
||||
|
||||
if (decoded['status'] == true) {
|
||||
ToastHelper.showSuccessToast(context, decoded['message']);
|
||||
serviceId = null;
|
||||
departmentList.clear();
|
||||
setState(() {
|
||||
isLoading = false;
|
||||
});
|
||||
setState(() => isLoading = false);
|
||||
context.go('/claims', extra: 2);
|
||||
print('Form data submitted successfully');
|
||||
fileService.clearAll();
|
||||
} else {
|
||||
setState(() {
|
||||
isLoading = false;
|
||||
});
|
||||
setState(() => isLoading = false);
|
||||
ToastHelper.showErrorToast(context, "Failed: ${decoded['message']}");
|
||||
}
|
||||
|
||||
|
||||
} catch (e) {
|
||||
setState(() {
|
||||
isLoading = false;
|
||||
});
|
||||
setState(() => isLoading = false);
|
||||
print('Error submitting form data: $e');
|
||||
} finally {
|
||||
setState(() => isSubmitting = false); // 🔥 stop loader
|
||||
setState(() => isSubmitting = false); // 🔥 always stop loader
|
||||
}
|
||||
}
|
||||
|
||||
@ -932,60 +931,92 @@ class _planclaimsformState extends State<planclaimsform> {
|
||||
canPop: false,
|
||||
onPopInvokedWithResult: (didPop, result) {
|
||||
if (didPop) return;
|
||||
final route = fromClaimsPage == 0 ? 'claims' : 'help';
|
||||
context.go('/$route');
|
||||
context.pop();
|
||||
},
|
||||
child: Scaffold(
|
||||
backgroundColor: Colors.white,
|
||||
appBar: CustomAppBar(),
|
||||
body: Stack(children: [
|
||||
SingleChildScrollView(
|
||||
child: Container(
|
||||
padding: Responsive.isDesktop(context)
|
||||
? EdgeInsets.symmetric(
|
||||
horizontal: MediaQuery.of(context).size.width *
|
||||
0.2, // 30% of screen width as horizontal padding
|
||||
vertical: MediaQuery.of(context).size.height *
|
||||
0.05, // 5% of screen height as vertical padding
|
||||
)
|
||||
: EdgeInsets.all(10),
|
||||
color: Colors.white,
|
||||
child: Column(children: [
|
||||
Card(
|
||||
elevation: 5,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius:
|
||||
BorderRadius.circular(15.0), // Set border radius here
|
||||
),
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white, // Set background color to white
|
||||
borderRadius: BorderRadius.circular(
|
||||
15.0), // Set border radius for Container
|
||||
),
|
||||
padding: Responsive.isDesktop(context)
|
||||
? EdgeInsets.only(top: 15, bottom: 15, left: 15, right: 15)
|
||||
: EdgeInsets.only(
|
||||
top: 10,
|
||||
bottom: 10,
|
||||
left: 10,
|
||||
right: 10), // Add padding to the container
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
flex: 12,
|
||||
child: Container(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Color(
|
||||
0xFFFFFCE5), // Set background color for the container
|
||||
borderRadius: BorderRadius.circular(
|
||||
10), // Set border radius for the container
|
||||
backgroundColor: Colors.white,
|
||||
appBar: CustomAppBar(),
|
||||
body: Stack(children: [
|
||||
SingleChildScrollView(
|
||||
child: Container(
|
||||
padding: Responsive.isDesktop(context)
|
||||
? EdgeInsets.symmetric(
|
||||
horizontal: MediaQuery.of(context).size.width *
|
||||
0.2, // 30% of screen width as horizontal padding
|
||||
vertical: MediaQuery.of(context).size.height *
|
||||
0.05, // 5% of screen height as vertical padding
|
||||
)
|
||||
: EdgeInsets.all(10),
|
||||
color: Colors.white,
|
||||
child: Column(children: [
|
||||
Card(
|
||||
elevation: 5,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius:
|
||||
BorderRadius.circular(15.0), // Set border radius here
|
||||
),
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white, // Set background color to white
|
||||
borderRadius: BorderRadius.circular(
|
||||
15.0), // Set border radius for Container
|
||||
),
|
||||
padding: Responsive.isDesktop(context)
|
||||
? EdgeInsets.only(top: 15, bottom: 15, left: 15, right: 15)
|
||||
: EdgeInsets.only(
|
||||
top: 10,
|
||||
bottom: 10,
|
||||
left: 10,
|
||||
right: 10), // Add padding to the container
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
flex: 12,
|
||||
child: Container(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Color(
|
||||
0xFFFFFCE5), // Set background color for the container
|
||||
borderRadius: BorderRadius.circular(
|
||||
10), // Set border radius for the container
|
||||
),
|
||||
padding: Responsive.isDesktop(context)
|
||||
? EdgeInsets.only(
|
||||
top: 20,
|
||||
bottom: 20,
|
||||
left: 0,
|
||||
right: 0)
|
||||
: EdgeInsets.only(
|
||||
top: 10,
|
||||
bottom: 10,
|
||||
left: 10,
|
||||
right: 10),
|
||||
child: Column(
|
||||
crossAxisAlignment:
|
||||
CrossAxisAlignment.center,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.start,
|
||||
children: [
|
||||
Expanded(
|
||||
flex: 1,
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
context.pop();
|
||||
},
|
||||
child: Icon(
|
||||
Icons
|
||||
.chevron_left, // Replace with your desired icon
|
||||
color: Color(0xFF000000),
|
||||
size: 30,
|
||||
),
|
||||
),
|
||||
),
|
||||
padding: Responsive.isDesktop(context)
|
||||
? EdgeInsets.only(
|
||||
@ -1039,19 +1070,439 @@ class _planclaimsformState extends State<planclaimsform> {
|
||||
fontSize:
|
||||
Responsive.isDesktop(
|
||||
context)
|
||||
? 20
|
||||
: 16,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Color(0xFF000000),
|
||||
? 20
|
||||
: 16,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Color(0xFF000000),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
)
|
||||
],
|
||||
), // Space between rows
|
||||
// Add more rows as needed
|
||||
],
|
||||
),
|
||||
),
|
||||
SizedBox(height: 15),
|
||||
Container(
|
||||
child: Column(
|
||||
children: [
|
||||
Column(
|
||||
crossAxisAlignment:
|
||||
CrossAxisAlignment.start,
|
||||
children: [
|
||||
Container(
|
||||
width: double.infinity,
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Column(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.start,
|
||||
crossAxisAlignment:
|
||||
CrossAxisAlignment.start,
|
||||
children: [
|
||||
Form(
|
||||
key: formKey,
|
||||
child: Column(
|
||||
children: [
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
buildDropdownField(
|
||||
'Service',isRequired: true,
|
||||
(value) {
|
||||
setState(() {
|
||||
serviceId = value;
|
||||
selectedServiceName = departmentList.firstWhere(
|
||||
(serList) => serList['id'] == value,
|
||||
orElse: () => {'name': ''},
|
||||
)['name'];
|
||||
policyNumberId = null;
|
||||
if (fromClaimsPage == 1) {
|
||||
fetchPoliciesBasedOnService(serviceId);
|
||||
}
|
||||
isServiceValid = true;
|
||||
});
|
||||
},
|
||||
fromClaimsPage == 0,
|
||||
departmentList,
|
||||
'name',
|
||||
serviceId,
|
||||
),
|
||||
if (!isServiceValid)
|
||||
Text('Please select a service', style: TextStyle(color: Colors.red)),
|
||||
],
|
||||
),
|
||||
|
||||
// buildDropdownField(
|
||||
// 'Service',
|
||||
// (value) {
|
||||
// setState(() {
|
||||
// serviceId = value;
|
||||
// selectedServiceName =
|
||||
// departmentList
|
||||
// .firstWhere(
|
||||
// (serList) =>
|
||||
// serList[
|
||||
// 'id'] ==
|
||||
// value,
|
||||
// orElse: () =>
|
||||
// {'name': ''},
|
||||
// )['name'];
|
||||
// policyNumberId =
|
||||
// null;
|
||||
// if (fromClaimsPage ==
|
||||
// 1) {
|
||||
// fetchPoliciesBasedOnService(
|
||||
// serviceId);
|
||||
// }
|
||||
// });
|
||||
// },
|
||||
// fromClaimsPage == 0,
|
||||
// departmentList,
|
||||
// 'name',
|
||||
// serviceId, // Pass current serviceId as selectedValue
|
||||
// ),
|
||||
SizedBox(height: 15),
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
buildDropdownField(
|
||||
'Select Policy',isRequired: true,
|
||||
(value) {
|
||||
setState(() {
|
||||
policyNumberId = value;
|
||||
selectedMemberId = null;
|
||||
String? selectedPolicyNo = policyNumberList.firstWhere(
|
||||
(item) => item['id'] == value,
|
||||
orElse: () => {'policy_no': ''},
|
||||
)['policy_no'];
|
||||
if (selectedPolicyNo != null && fromClaimsPage == 1) {
|
||||
fetchMemberBasedOnPolicy(selectedPolicyNo);
|
||||
}
|
||||
isPolicyValid = true;
|
||||
});
|
||||
},
|
||||
fromClaimsPage == 0,
|
||||
policyNumberList,
|
||||
'policy_no',
|
||||
policyNumberId,
|
||||
),
|
||||
if (!isPolicyValid)
|
||||
Text('Please select a policy', style: TextStyle(color: Colors.red)),
|
||||
],
|
||||
),
|
||||
|
||||
// // if (fromClaimsPage == 1)
|
||||
// buildDropdownField(
|
||||
// 'Select Policy',
|
||||
// (value) {
|
||||
// setState(() {
|
||||
// policyNumberId =
|
||||
// value;
|
||||
// selectedMemberId =
|
||||
// null;
|
||||
// String?
|
||||
// selectedPolicyNo =
|
||||
// policyNumberList
|
||||
// .firstWhere(
|
||||
// (item) =>
|
||||
// item['id'] ==
|
||||
// value,
|
||||
// orElse: () =>
|
||||
// {
|
||||
// 'policy_no': ''
|
||||
// })['policy_no'];
|
||||
//
|
||||
// if (selectedPolicyNo !=
|
||||
// null &&
|
||||
// fromClaimsPage ==
|
||||
// 1) {
|
||||
// fetchMemberBasedOnPolicy(
|
||||
// selectedPolicyNo);
|
||||
// }
|
||||
// });
|
||||
// },
|
||||
// fromClaimsPage == 0,
|
||||
// policyNumberList,
|
||||
// 'policy_no',
|
||||
// policyNumberId, // Pass current policyNumberId as selectedValue
|
||||
// ),
|
||||
SizedBox(height: 15),
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
buildDropdownField(
|
||||
'Member Name',isRequired: true,
|
||||
(value) {
|
||||
setState(() {
|
||||
selectedMemberId = value;
|
||||
selectedMemberName = employeePolicyList.firstWhere(
|
||||
(member) => member['id'] == value,
|
||||
orElse: () => {'name': ''},
|
||||
)['name'];
|
||||
isMemberValid = true;
|
||||
});
|
||||
},
|
||||
false,
|
||||
employeePolicyList,
|
||||
'name',
|
||||
selectedMemberId,
|
||||
),
|
||||
if (!isMemberValid)
|
||||
Text('Please select a member', style: TextStyle(color: Colors.red)),
|
||||
],
|
||||
),
|
||||
SizedBox(height: 15),
|
||||
// buildDropdownField(
|
||||
// 'Member Name',
|
||||
// (value) {
|
||||
// setState(() {
|
||||
// selectedMemberId =
|
||||
// value;
|
||||
// selectedMemberName =
|
||||
// employeePolicyList
|
||||
// .firstWhere(
|
||||
// (member) =>
|
||||
// member[
|
||||
// 'id'] ==
|
||||
// value,
|
||||
// orElse: () =>
|
||||
// {'name': ''},
|
||||
// )['name'];
|
||||
// });
|
||||
// },
|
||||
// false,
|
||||
// employeePolicyList,
|
||||
// 'name',
|
||||
// selectedMemberId, // Pass current selectedMemberId as selectedValue
|
||||
// ),
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// buildTextField('Subject',
|
||||
// subjectController),
|
||||
// if (!isSubjectValid)
|
||||
// Text('Please enter the Subject', style: TextStyle(color: Colors.red)),
|
||||
SizedBox(height: 15),
|
||||
buildTextAreaField(
|
||||
'Message',
|
||||
messageController),
|
||||
SizedBox(height: 15),
|
||||
// if (policyTypeCondition ==
|
||||
// 3)
|
||||
// buildTextField(
|
||||
// 'Accident Details',
|
||||
// accidentDetailsController),
|
||||
SizedBox(height: 15),
|
||||
if (policyTypeCondition == 1 || policyTypeCondition == 72) ...[
|
||||
buildTextField('Hospital Name', hospitalNameController, isRequired: true),
|
||||
if (!isHospitalNameValid)
|
||||
Text('Please enter the Hospital Name', style: TextStyle(color: Colors.red)),
|
||||
SizedBox(height: 15),
|
||||
buildTextAreaField('Hospital Address', hospitalAddressController, isRequired: true),
|
||||
if (!isHospitalAddressValid)
|
||||
Text('Please enter the Hospital Address', style: TextStyle(color: Colors.red)),
|
||||
SizedBox(height: 15),
|
||||
buildTextField('Hospital City', hospitalCityController,isRequired: true,inputFormatters: [FilteringTextInputFormatter.allow(RegExp(r'[a-zA-Z ]'))]),
|
||||
if (!isHospitalCityValid)
|
||||
Text('Please enter the Hospital City', style: TextStyle(color: Colors.red)),
|
||||
SizedBox(height: 15),
|
||||
buildTextField('Hospital State', hospitalStateController,isRequired: true,inputFormatters: [FilteringTextInputFormatter.allow(RegExp(r'[a-zA-Z ]'))]),
|
||||
if (!isHospitalStateValid)
|
||||
Text('Please enter the Hospital State', style: TextStyle(color: Colors.red)),
|
||||
SizedBox(height: 15),
|
||||
buildTextField('Hospital Pincode', hospitalPinCodeController,isRequired: true,keyboardType: TextInputType.number,
|
||||
inputFormatters: [
|
||||
FilteringTextInputFormatter.digitsOnly,
|
||||
LengthLimitingTextInputFormatter(6),
|
||||
]),
|
||||
if (!isHospitalPincodeValid)
|
||||
Text('Please enter the Hospital Pincode', style: TextStyle(color: Colors.red)),
|
||||
SizedBox(height: 15),
|
||||
buildTextField('Hospital Phone No', hospitalPhoneNoController,
|
||||
isRequired: true,
|
||||
keyboardType: TextInputType.number,
|
||||
inputFormatters: [
|
||||
FilteringTextInputFormatter.digitsOnly,
|
||||
LengthLimitingTextInputFormatter(10),
|
||||
],minLength: 10,),
|
||||
if (!isHospitalPhoneNoValid)
|
||||
Text(
|
||||
hospitalPhoneNoController.text.trim().isEmpty
|
||||
? 'Please enter the Hospital Phone No'
|
||||
: 'Hospital Phone No must be 10 digits', // ✅ specific message
|
||||
style: TextStyle(color: Colors.red),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
)
|
||||
],
|
||||
), // Space between rows
|
||||
// Add more rows as needed
|
||||
],
|
||||
SizedBox(height: 15),
|
||||
if (serviceId == 2 ||
|
||||
serviceId == 3 ||
|
||||
serviceId == 4) ...[
|
||||
buildDatePickerField(
|
||||
label:
|
||||
'Date of Birth',
|
||||
selectedDate:
|
||||
birthDate,
|
||||
allowFuture: false,
|
||||
onDateSelected:
|
||||
(selectedDate) {
|
||||
setState(() {
|
||||
birthDate =
|
||||
selectedDate;
|
||||
});
|
||||
},
|
||||
),
|
||||
SizedBox(height: 15),
|
||||
buildDatePickerField(
|
||||
label: 'Accident Date',
|
||||
isRequired: true,
|
||||
selectedDate: accidentDate,
|
||||
allowFuture: false,
|
||||
minDate: parsedPolicyStartDate,
|
||||
maxDate: parsedPolicyEndDate,
|
||||
onDateSelected: (selectedDate) {
|
||||
setState(() {
|
||||
if (deathDate != null) deathDate = null;
|
||||
if (intimationDate != null) intimationDate = null;
|
||||
accidentDate = selectedDate;
|
||||
});
|
||||
},
|
||||
),
|
||||
if (!isAccidentDateValid)
|
||||
Text('Please select the Accident Date', style: TextStyle(color: Colors.red)),
|
||||
SizedBox(height: 15),
|
||||
buildDatePickerField(
|
||||
label:
|
||||
'Date of Death',
|
||||
selectedDate:
|
||||
deathDate,
|
||||
isRequired: true,
|
||||
allowFuture: false,
|
||||
minDate: accidentDate ?? parsedPolicyStartDate,
|
||||
maxDate: parsedPolicyEndDate,
|
||||
onDateSelected:
|
||||
(selectedDate) {
|
||||
setState(() {
|
||||
deathDate =
|
||||
selectedDate;
|
||||
});
|
||||
},
|
||||
),
|
||||
SizedBox(height: 15),
|
||||
buildDatePickerField(
|
||||
label: 'Date of Intimation',
|
||||
isRequired: true,
|
||||
selectedDate: intimationDate,
|
||||
allowFuture: false,
|
||||
minDate: accidentDate ?? deathDate ?? parsedPolicyStartDate,
|
||||
maxDate: parsedPolicyEndDate,
|
||||
onDateSelected: (selectedDate) {
|
||||
setState(() {
|
||||
intimationDate = selectedDate;
|
||||
});
|
||||
},
|
||||
),
|
||||
if (!isIntimationDateValid)
|
||||
Text('Please select the Intimation Date', style: TextStyle(color: Colors.red)),
|
||||
],
|
||||
SizedBox(height: 15),
|
||||
// buildTextAreaField(
|
||||
// 'Accident Details',
|
||||
// accidentDetailsController),
|
||||
// SizedBox(height: 15),
|
||||
if (policyTypeCondition == 1 || policyTypeCondition == 72) ...[
|
||||
// 🟡 Admit Date — no future allowed
|
||||
buildDatePickerField(
|
||||
label: 'Admit Date',
|
||||
isRequired: true,
|
||||
selectedDate: admitDate,
|
||||
allowFuture: false,
|
||||
onDateSelected: (selectedDate) {
|
||||
setState(() {
|
||||
admitDate = selectedDate;
|
||||
dischargeDate = null; // reset discharge when admit changes
|
||||
});
|
||||
},
|
||||
),
|
||||
if (!isAdmitDateValid)
|
||||
const Text('Please select the Admit Date', style: TextStyle(color: Colors.red)),
|
||||
const SizedBox(height: 20),
|
||||
|
||||
// 🟢 Discharge Date — must be after Admit Date
|
||||
buildDatePickerField(
|
||||
label: 'Discharge Date',
|
||||
isRequired: true,
|
||||
selectedDate: dischargeDate,
|
||||
allowFuture: true,
|
||||
minDate: admitDate != null
|
||||
? admitDate!.add(const Duration(days: 1))
|
||||
: DateTime.now().add(const Duration(days: 1)),
|
||||
maxDate: null,
|
||||
onDateSelected: (selectedDate) {
|
||||
setState(() {
|
||||
dischargeDate = selectedDate;
|
||||
});
|
||||
},
|
||||
),
|
||||
if (!isDischargeDateValid)
|
||||
const Text('Please select the Discharge Date', style: TextStyle(color: Colors.red)),
|
||||
],
|
||||
|
||||
|
||||
SizedBox(height: 15),
|
||||
if (serviceId == 1) ...[
|
||||
buildTextField('Claims Amount', claimAmountController, isRequired: true, keyboardType: TextInputType.number,
|
||||
inputFormatters: [
|
||||
FilteringTextInputFormatter.digitsOnly,
|
||||
]),
|
||||
if (!isClaimAmountValid)
|
||||
Text('Please enter the Claims Amount', style: TextStyle(color: Colors.red)),
|
||||
],
|
||||
|
||||
SizedBox(height: 15),
|
||||
if (serviceId == 2 ||
|
||||
serviceId == 3 ||
|
||||
serviceId == 4)
|
||||
buildTextField(
|
||||
'Sum Insured',
|
||||
sumInsuredController,
|
||||
isRequired: true,
|
||||
keyboardType: TextInputType.number,
|
||||
inputFormatters: [
|
||||
FilteringTextInputFormatter.digitsOnly,
|
||||
],
|
||||
),
|
||||
]
|
||||
),
|
||||
SizedBox(height: 15),
|
||||
// ThemedUploadField(
|
||||
// hintText: "Upload Documents",
|
||||
// txtwidth: MediaQuery.of(context).size.width < 600
|
||||
// ? MediaQuery.of(context).size.width // Full width on mobile
|
||||
// : MediaQuery.of(context).size.width * 0.26, // 26% on desktop
|
||||
// txtheight: 45,
|
||||
// onFilesSelected: (files) {
|
||||
// print("Picked files: ${files.map((f) => f.name).toList()}");
|
||||
// setState(() {
|
||||
// uploadedFiles = files; // store all selected files
|
||||
// });
|
||||
// },
|
||||
// ),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Column(
|
||||
children: [
|
||||
MultiFileUploadWidget(isRequired: true,showError: showFileError),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(height: 15),
|
||||
@ -1615,32 +2066,38 @@ class _planclaimsformState extends State<planclaimsform> {
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Widget buildTextField(
|
||||
String label,
|
||||
TextEditingController controller, {
|
||||
bool isRequired = false, // 👈 ADD THIS
|
||||
TextInputType keyboardType = TextInputType.text,
|
||||
List<TextInputFormatter>? inputFormatters,
|
||||
int? minLength,
|
||||
}) {
|
||||
return TextFormField(
|
||||
controller: controller,
|
||||
keyboardType: keyboardType,
|
||||
inputFormatters: inputFormatters,
|
||||
decoration: InputDecoration(labelText: label),
|
||||
decoration: InputDecoration(
|
||||
labelText: isRequired ? '$label *' : label, // 👈 ADD THIS
|
||||
),
|
||||
validator: (value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
return 'Please enter the $label';
|
||||
}
|
||||
if (minLength != null && value.trim().length < minLength) { // 👈 ADD this
|
||||
return '$label must be at least $minLength digits';
|
||||
}
|
||||
return null;
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Widget buildTextAreaField(String label, TextEditingController controller) {
|
||||
Widget buildTextAreaField(String label, TextEditingController controller ,{ bool isRequired = false}) {
|
||||
return TextFormField(
|
||||
controller: controller,
|
||||
decoration: InputDecoration(labelText: label),
|
||||
decoration: InputDecoration(labelText: isRequired ? '$label *' : label),
|
||||
maxLines: 5,
|
||||
validator: (value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
@ -1656,6 +2113,7 @@ class _planclaimsformState extends State<planclaimsform> {
|
||||
required DateTime? selectedDate,
|
||||
required bool allowFuture,
|
||||
required ValueChanged<DateTime?> onDateSelected,
|
||||
bool isRequired = false,
|
||||
DateTime? minDate,
|
||||
DateTime? maxDate,
|
||||
}) {
|
||||
@ -1706,7 +2164,7 @@ class _planclaimsformState extends State<planclaimsform> {
|
||||
},
|
||||
child: InputDecorator(
|
||||
decoration: InputDecoration(
|
||||
labelText: label,
|
||||
labelText: isRequired ? '$label *' : label,
|
||||
border: const OutlineInputBorder(),
|
||||
),
|
||||
child: Text(
|
||||
@ -1726,11 +2184,12 @@ class _planclaimsformState extends State<planclaimsform> {
|
||||
bool readOnly,
|
||||
List<Map<String, dynamic>> itemsList,
|
||||
String displayField,
|
||||
int? selectedValue // Added selectedValue parameter
|
||||
int? selectedValue, // Added selectedValue parameter
|
||||
{bool isRequired = false,}
|
||||
) {
|
||||
return DropdownButtonFormField<int>(
|
||||
value: selectedValue,
|
||||
decoration: InputDecoration(labelText: label),
|
||||
decoration: InputDecoration(labelText: isRequired ? '$label *' : label,),
|
||||
items: itemsList.map<DropdownMenuItem<int>>((item) {
|
||||
return DropdownMenuItem<int>(
|
||||
value: item['id'], // Ensure 'id' is correctly referenced
|
||||
|
||||
@ -330,12 +330,8 @@ class ApiService {
|
||||
}
|
||||
|
||||
Future<void> _clearLocalStorageAndRedirect() async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
await prefs.clear();
|
||||
// Assuming you have access to the context
|
||||
ToastHelper.showErrorToast(context, 'Session Out');
|
||||
context.go('/login');
|
||||
// Navigator.pushNamed(context, 'login');
|
||||
if (!context.mounted) return;
|
||||
await TokenService().logout(context);
|
||||
}
|
||||
|
||||
Future<Map<String, dynamic>> getBotDetails(
|
||||
@ -425,7 +421,12 @@ class ApiService {
|
||||
ToastHelper.showWarningToast(context, message);
|
||||
return {};
|
||||
} else if (response.statusCode == 429) {
|
||||
<<<<<<< HEAD
|
||||
final body = jsonDecode(response.body);
|
||||
=======
|
||||
if (!context.mounted) return {};
|
||||
final body = jsonDecode(response.body);
|
||||
>>>>>>> 24f532f750daddbb5d414d9056a4e1c4c760a2c4
|
||||
final message = body['message'];
|
||||
ToastHelper.showWarningToast(context, message);
|
||||
return {};
|
||||
|
||||
@ -4,8 +4,15 @@ import 'file_upload_service.dart';
|
||||
|
||||
class MultiFileUploadWidget extends StatefulWidget {
|
||||
final bool forceMobile;
|
||||
final bool isRequired;
|
||||
final bool showError; // ✅ ADD THIS
|
||||
|
||||
const MultiFileUploadWidget({super.key, this.forceMobile = false});
|
||||
const MultiFileUploadWidget({
|
||||
super.key,
|
||||
this.forceMobile = false,
|
||||
this.isRequired = false,
|
||||
this.showError = false, // ✅ ADD THIS
|
||||
});
|
||||
|
||||
@override
|
||||
State<MultiFileUploadWidget> createState() => _MultiFileUploadWidgetState();
|
||||
@ -18,27 +25,12 @@ class _MultiFileUploadWidgetState extends State<MultiFileUploadWidget> {
|
||||
String? errorMessage;
|
||||
|
||||
void _pickFiles() async {
|
||||
final error = await fileService.pickFiles(maxFileSizeInMB: 10); // 5 MB limit
|
||||
final error = await fileService.pickFiles(maxFileSizeInMB: 10);
|
||||
if (error != null) {
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
errorMessage = error;
|
||||
});
|
||||
|
||||
// also show alert dialog for big error messages
|
||||
// showDialog(
|
||||
// context: context,
|
||||
// builder: (ctx) => AlertDialog(
|
||||
// title: const Text("File Upload Error"),
|
||||
// content: Text(error),
|
||||
// actions: [
|
||||
// TextButton(
|
||||
// onPressed: () => Navigator.pop(ctx),
|
||||
// child: const Text("OK"),
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// );
|
||||
}
|
||||
} else {
|
||||
setState(() {
|
||||
@ -55,6 +47,31 @@ class _MultiFileUploadWidgetState extends State<MultiFileUploadWidget> {
|
||||
});
|
||||
}
|
||||
|
||||
// ✅ Build the button label with RichText to show * in red
|
||||
Widget _buildButtonLabel() {
|
||||
if (!widget.isRequired) {
|
||||
return const Text(
|
||||
"Upload Documents",
|
||||
style: TextStyle(fontSize: 14, color: Colors.black),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
);
|
||||
}
|
||||
return RichText(
|
||||
text: const TextSpan(
|
||||
children: [
|
||||
TextSpan(
|
||||
text: "Upload Documents ",
|
||||
style: TextStyle(fontSize: 14, color: Colors.black),
|
||||
),
|
||||
TextSpan(
|
||||
text: "*", // ✅ red * like other fields
|
||||
style: TextStyle(fontSize: 14, color: Colors.red),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final files = fileService.files;
|
||||
@ -67,21 +84,14 @@ class _MultiFileUploadWidgetState extends State<MultiFileUploadWidget> {
|
||||
onPressed: _pickFiles,
|
||||
icon: const Icon(Icons.file_upload_outlined,
|
||||
color: Color(0xFFE26728), size: 24),
|
||||
label: const Text(
|
||||
"Upload Documents",
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: Colors.black,
|
||||
),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
label: _buildButtonLabel(), // ✅ use rich text label
|
||||
style: OutlinedButton.styleFrom(
|
||||
side: const BorderSide(color: Color(0xFFE26728)),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 6),
|
||||
const Text(
|
||||
"Supports only PDF, PNG, JPG, JPEG,HEIC formats (max 10 MB each)",
|
||||
"Supports only PDF, PNG, JPG, JPEG, HEIC formats (max 10 MB each)",
|
||||
style: TextStyle(fontSize: 12, color: Colors.grey),
|
||||
),
|
||||
] else ...[
|
||||
@ -91,14 +101,7 @@ class _MultiFileUploadWidgetState extends State<MultiFileUploadWidget> {
|
||||
onPressed: _pickFiles,
|
||||
icon: const Icon(Icons.file_upload_outlined,
|
||||
color: Color(0xFFE26728), size: 24),
|
||||
label: const Text(
|
||||
"Upload Documents",
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: Colors.black,
|
||||
),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
label: _buildButtonLabel(), // ✅ use rich text label
|
||||
style: OutlinedButton.styleFrom(
|
||||
side: const BorderSide(color: Color(0xFFE26728)),
|
||||
),
|
||||
@ -106,7 +109,7 @@ class _MultiFileUploadWidgetState extends State<MultiFileUploadWidget> {
|
||||
const SizedBox(width: 12),
|
||||
const Expanded(
|
||||
child: Text(
|
||||
"Supports only PDF, PNG, JPG, JPEG,HEIC formats (max 10 MB each)",
|
||||
"Supports only PDF, PNG, JPG, JPEG, HEIC formats (max 10 MB each)",
|
||||
style: TextStyle(fontSize: 12, color: Colors.grey),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
@ -115,10 +118,11 @@ class _MultiFileUploadWidgetState extends State<MultiFileUploadWidget> {
|
||||
),
|
||||
],
|
||||
|
||||
if (fileService.files.isEmpty && errorMessage == null) ...[
|
||||
// ✅ Show error only when required AND no files
|
||||
if (widget.isRequired && widget.showError && files.isEmpty && errorMessage == null) ...[
|
||||
const SizedBox(height: 4),
|
||||
const Text(
|
||||
"Required",
|
||||
'Please upload the document',
|
||||
style: TextStyle(color: Colors.red, fontSize: 12),
|
||||
),
|
||||
],
|
||||
@ -134,7 +138,7 @@ class _MultiFileUploadWidgetState extends State<MultiFileUploadWidget> {
|
||||
const SizedBox(height: 8),
|
||||
...fileService.files.asMap().entries.map((entry) {
|
||||
final index = entry.key;
|
||||
final uploaded = entry.value; // UploadedFile
|
||||
final uploaded = entry.value;
|
||||
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
@ -142,7 +146,8 @@ class _MultiFileUploadWidgetState extends State<MultiFileUploadWidget> {
|
||||
ListTile(
|
||||
dense: true,
|
||||
contentPadding: EdgeInsets.zero,
|
||||
title: Text(uploaded.file.name, style: const TextStyle(fontSize: 14)),
|
||||
title: Text(uploaded.file.name,
|
||||
style: const TextStyle(fontSize: 14)),
|
||||
trailing: IconButton(
|
||||
icon: const Icon(Icons.close, color: Colors.red),
|
||||
onPressed: () => _removeFile(index),
|
||||
|
||||
@ -66,6 +66,14 @@ class _setPasswordState extends State<setPassword> {
|
||||
final confirmPassword = confirmPasswordController.text.trim();
|
||||
try {
|
||||
if (_formKey.currentState!.validate()) {
|
||||
final strongPasswordRegex =
|
||||
RegExp(r'^(?=.*[A-Za-z])(?=.*\d)(?=.*[@$!%*#?&]).{8,}$');
|
||||
if (!strongPasswordRegex.hasMatch(newPassword)) {
|
||||
ToastHelper.showErrorToast(
|
||||
context,
|
||||
'Enter a valid password (8+ chars with letter, number, special char)');
|
||||
return;
|
||||
}
|
||||
if (confirmPassword != newPassword) {
|
||||
ToastHelper.showErrorToast(context, 'Passwords do not match');
|
||||
return;
|
||||
@ -213,15 +221,20 @@ class _setPasswordState extends State<setPassword> {
|
||||
}
|
||||
return WillPopScope(
|
||||
onWillPop: () async {
|
||||
// Close the app on mobile back button press
|
||||
exit(0); // This will exit the app
|
||||
return false; // Return false to prevent any other actions
|
||||
// 🖥 Desktop / Web → always go back to login
|
||||
if (kIsWeb || Responsive.isDesktop(context)) {
|
||||
context.go('/login');
|
||||
return false;
|
||||
}
|
||||
// 📱 Mobile → keep existing behavior (exit app)
|
||||
exit(0);
|
||||
return false;
|
||||
},
|
||||
child: Scaffold(
|
||||
body: SingleChildScrollView(
|
||||
keyboardDismissBehavior: ScrollViewKeyboardDismissBehavior.onDrag,
|
||||
child: Container(
|
||||
height: _size.height,
|
||||
constraints: BoxConstraints(minHeight: _size.height),
|
||||
color: Colors.white,
|
||||
child: Stack(
|
||||
children: [
|
||||
@ -308,7 +321,7 @@ class _setPasswordState extends State<setPassword> {
|
||||
// ),
|
||||
Container(
|
||||
margin: marginInsets,
|
||||
alignment: Alignment.bottomCenter,
|
||||
alignment: Alignment.center,
|
||||
child: SingleChildScrollView(
|
||||
child: Form(
|
||||
key: _formKey,
|
||||
@ -353,8 +366,7 @@ class _setPasswordState extends State<setPassword> {
|
||||
Expanded(
|
||||
flex: 12,
|
||||
child: Align(
|
||||
alignment: Alignment
|
||||
.topLeft, // ✅ Always top-left
|
||||
alignment: Alignment.center,
|
||||
child: _size.width <= 1100
|
||||
? Image.asset(
|
||||
'assets/nhance_app_logo.png',
|
||||
@ -378,10 +390,9 @@ class _setPasswordState extends State<setPassword> {
|
||||
),
|
||||
SizedBox(
|
||||
height: Responsive.isDesktop(context)
|
||||
? _size.height * 0.1
|
||||
: 10,
|
||||
? null
|
||||
: _size.height * 0.2,
|
||||
),
|
||||
SizedBox(height: 10),
|
||||
Container(
|
||||
margin: Responsive.isDesktop(context)
|
||||
? EdgeInsets.symmetric(
|
||||
@ -464,6 +475,25 @@ class _setPasswordState extends State<setPassword> {
|
||||
},
|
||||
),
|
||||
),
|
||||
validator: (value) {
|
||||
final password = value ?? '';
|
||||
if (password.isEmpty) {
|
||||
return 'Please enter your new password';
|
||||
}
|
||||
if (password.length < 8) {
|
||||
return 'Password must be at least 8 characters';
|
||||
}
|
||||
if (!RegExp(r'[A-Za-z]').hasMatch(password)) {
|
||||
return 'Password must include at least 1 letter';
|
||||
}
|
||||
if (!RegExp(r'\d').hasMatch(password)) {
|
||||
return 'Password must include at least 1 number';
|
||||
}
|
||||
if (!RegExp(r'[@$!%*#?&]').hasMatch(password)) {
|
||||
return 'Password must include at least 1 special character';
|
||||
}
|
||||
return null;
|
||||
},
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
@ -604,10 +634,10 @@ class _setPasswordState extends State<setPassword> {
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
// _size.height * 0.3
|
||||
SizedBox(
|
||||
height: Responsive.isDesktop(context)
|
||||
? _size.height * 0.3
|
||||
? null
|
||||
: _size.height * 0.2,
|
||||
),
|
||||
// SizedBox(
|
||||
|
||||
Loading…
Reference in New Issue
Block a user