1063 lines
43 KiB
Dart
1063 lines
43 KiB
Dart
import 'dart:convert';
|
|
import 'dart:html' as html;
|
|
// import 'dart:ui' as html;
|
|
import 'package:flutter/foundation.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:fluttertoast/fluttertoast.dart';
|
|
import 'package:frontend/config/apiUrl.dart';
|
|
import 'package:google_fonts/google_fonts.dart';
|
|
import 'package:http/http.dart' as http;
|
|
import 'package:go_router/go_router.dart';
|
|
import 'package:shared_preferences/shared_preferences.dart';
|
|
import 'package:url_launcher/url_launcher.dart';
|
|
|
|
import '../../../services/apiService.dart';
|
|
|
|
class LoginWidget extends StatefulWidget {
|
|
final bool isDesktop;
|
|
final bool isTablet;
|
|
|
|
const LoginWidget({Key? key, required this.isDesktop, required this.isTablet})
|
|
: super(key: key);
|
|
|
|
@override
|
|
_LoginWidgetState createState() => _LoginWidgetState();
|
|
}
|
|
|
|
enum LoginStep { login, forgotEmail, otpReset }
|
|
|
|
class _LoginWidgetState extends State<LoginWidget> {
|
|
final ApiService apiService = ApiService();
|
|
bool _moved = false;
|
|
|
|
final _formKey = GlobalKey<FormState>();
|
|
final TextEditingController _emailController = TextEditingController();
|
|
final TextEditingController _passwordController = TextEditingController();
|
|
final TextEditingController _otpController = TextEditingController();
|
|
final TextEditingController _newPasswordController = TextEditingController();
|
|
final TextEditingController _confirmPasswordController =
|
|
TextEditingController();
|
|
String? userRole;
|
|
bool _obscureText = true;
|
|
bool _isForgotPassword = false;
|
|
bool _showOtpResetFields = false;
|
|
// 🔹 Login Step Enum and State Variable
|
|
LoginStep _loginStep = LoginStep.login;
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
|
|
Future.delayed(Duration(milliseconds: 300), () {
|
|
setState(() {
|
|
_moved = true;
|
|
});
|
|
});
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
_emailController.dispose();
|
|
_passwordController.dispose();
|
|
_otpController.dispose();
|
|
_newPasswordController.dispose();
|
|
_confirmPasswordController.dispose();
|
|
super.dispose();
|
|
}
|
|
|
|
Future<void> storeUserDetails(String token) async {
|
|
try {
|
|
final parts = token.split('.');
|
|
if (parts.length != 3) throw Exception('Invalid token format');
|
|
|
|
final payload = json.decode(
|
|
utf8.decode(base64Url.decode(base64Url.normalize(parts[1]))),
|
|
);
|
|
|
|
final userData = payload['data'];
|
|
|
|
final prefs = await SharedPreferences.getInstance();
|
|
await prefs.setString('auth_token', token);
|
|
await prefs.setString(
|
|
'user_data',
|
|
jsonEncode(userData),
|
|
); // Store full user data
|
|
|
|
if (userData != null) {
|
|
final pref = await SharedPreferences.getInstance();
|
|
await pref.setString('auth_token', token);
|
|
await pref.setString('user_data', jsonEncode(userData));
|
|
|
|
userRole = userData['role'];
|
|
|
|
print("userData - $userData");
|
|
print("userData11 - ${userData['role']}");
|
|
print("userData12 - $userRole");
|
|
}
|
|
|
|
await apiService.getOrganizationData();
|
|
} catch (e) {
|
|
print('Error decoding token: $e');
|
|
}
|
|
}
|
|
|
|
Future<void> _login(BuildContext context) async {
|
|
if (_formKey.currentState!.validate()) {
|
|
const String url = '$apiUrl/auth/login';
|
|
|
|
try {
|
|
final response = await http.post(
|
|
Uri.parse(url),
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'Accept': 'application/json',
|
|
},
|
|
body: jsonEncode({
|
|
'email': _emailController.text.trim(),
|
|
'password': _passwordController.text.trim(),
|
|
}),
|
|
);
|
|
|
|
if (response.statusCode == 200) {
|
|
final data = jsonDecode(response.body);
|
|
print("data- $data");
|
|
|
|
final token = data['token']; // Assuming the token is in response
|
|
// final userId = data['user_id'].toString();
|
|
|
|
await storeUserDetails(token);
|
|
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
SnackBar(
|
|
content: Text("Login Successful"),
|
|
backgroundColor: Colors.green, // Set background to green
|
|
),
|
|
);
|
|
|
|
if (userRole == "Travel Agent") {
|
|
context.go('/listTravelAgentPlan');
|
|
} else if (userRole == "Org Admin" || userRole == "Travel Admin") {
|
|
context.go('/StatusDashboard');
|
|
} else {
|
|
context.go('/listPlan');
|
|
}
|
|
} else {
|
|
final body = jsonDecode(response.body);
|
|
final messages = body['messages'];
|
|
|
|
String errorMessage = 'Unknown error';
|
|
|
|
if (messages is Map && messages.isNotEmpty) {
|
|
errorMessage = messages.values.first.toString();
|
|
}
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
SnackBar(
|
|
content: Text("Login Failed: $errorMessage"),
|
|
backgroundColor: Colors.red,
|
|
),
|
|
);
|
|
_emailController.clear();
|
|
_passwordController.clear();
|
|
// Fluttertoast.showToast(
|
|
// msg: "Login Failed: $errorMessage",
|
|
// toastLength: Toast.LENGTH_LONG,
|
|
// gravity: ToastGravity.TOP_RIGHT,
|
|
// backgroundColor: Colors.red,
|
|
// textColor: Colors.white,
|
|
// fontSize: 16.0,
|
|
// );
|
|
}
|
|
} catch (e) {
|
|
ScaffoldMessenger.of(
|
|
context,
|
|
).showSnackBar(SnackBar(content: Text("Error: $e")));
|
|
}
|
|
}
|
|
}
|
|
|
|
Future<void> _handleSubmit(BuildContext context) async {
|
|
print('2');
|
|
if (!_formKey.currentState!.validate()) return;
|
|
print('3');
|
|
if (_isForgotPassword && !_showOtpResetFields) {
|
|
print('11');
|
|
// Step 1: Send OTP
|
|
final url = '$apiUrl/forgotPassword/verifyUser';
|
|
try {
|
|
final response = await http.post(
|
|
Uri.parse(url),
|
|
headers: {'Content-Type': 'application/json'},
|
|
body: jsonEncode({'email': _emailController.text.trim()}),
|
|
);
|
|
|
|
if (response.statusCode == 200) {
|
|
setState(() => _showOtpResetFields = true);
|
|
setState(() {
|
|
_isForgotPassword = false;
|
|
_showOtpResetFields = true;
|
|
// _clearAllFields();
|
|
});
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
SnackBar(
|
|
content: Text("OTP sent to your email"),
|
|
backgroundColor: Colors.green,
|
|
),
|
|
);
|
|
} else {
|
|
print(response);
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
SnackBar(
|
|
content: Text(
|
|
"${jsonDecode(response.body)['messages']['error']}",
|
|
),
|
|
|
|
backgroundColor: Colors.red,
|
|
),
|
|
);
|
|
}
|
|
} catch (e) {
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
SnackBar(content: Text("Error: $e"), backgroundColor: Colors.red),
|
|
);
|
|
}
|
|
} else if (!_isForgotPassword && _showOtpResetFields) {
|
|
print('22');
|
|
// Step 2: Verify OTP & Reset Password
|
|
final url = '$apiUrl/forgotPassword/changePassword';
|
|
try {
|
|
print('21');
|
|
final response = await http.post(
|
|
Uri.parse(url),
|
|
headers: {'Content-Type': 'application/json'},
|
|
body: jsonEncode({
|
|
'email': _emailController.text.trim(),
|
|
'otp': _otpController.text.trim(),
|
|
'new_password': _newPasswordController.text.trim(),
|
|
'confirm_password': _confirmPasswordController.text.trim(),
|
|
}),
|
|
);
|
|
|
|
if (response.statusCode == 200) {
|
|
print('22');
|
|
setState(() {
|
|
_isForgotPassword = false;
|
|
_showOtpResetFields = false;
|
|
_clearAllFields();
|
|
});
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
SnackBar(
|
|
content: Text("Password reset successfully"),
|
|
backgroundColor: Colors.green,
|
|
),
|
|
);
|
|
} else {
|
|
print('23');
|
|
print('otp wrong');
|
|
final responseBody = json.decode(response.body);
|
|
final errorMessage =
|
|
responseBody['messages']?['error'] ?? 'An unknown error occurred';
|
|
print(errorMessage);
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
SnackBar(
|
|
content: Text(
|
|
"Reset failed: $errorMessage",
|
|
// "Reset failed: ${jsonDecode(response.body)['message']}",
|
|
),
|
|
backgroundColor: Colors.red,
|
|
),
|
|
);
|
|
}
|
|
} catch (e) {
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
SnackBar(content: Text("Error: $e"), backgroundColor: Colors.red),
|
|
);
|
|
}
|
|
} else {
|
|
// Login flow
|
|
_login(context);
|
|
}
|
|
}
|
|
|
|
void _clearAllFields() {
|
|
_emailController.clear();
|
|
_passwordController.clear();
|
|
_otpController.clear();
|
|
_newPasswordController.clear();
|
|
_confirmPasswordController.clear();
|
|
}
|
|
|
|
void _onSubmit(BuildContext context) async {
|
|
if (_formKey.currentState!.validate()) {
|
|
if (!_isForgotPassword && !_showOtpResetFields) {
|
|
await _login(context);
|
|
} else {
|
|
print('1');
|
|
_handleSubmit(context);
|
|
}
|
|
}
|
|
}
|
|
|
|
@override
|
|
/// Layout
|
|
Widget build(BuildContext context) {
|
|
double formWidth = widget.isTablet ? 400 : 300;
|
|
|
|
return Container(
|
|
// color: Color(0xFF114D8B),
|
|
color: Colors.white,
|
|
|
|
// color: Color(0xFFf5f5f5),
|
|
padding: const EdgeInsets.all(10),
|
|
child: Row(
|
|
children: [
|
|
if (widget.isDesktop)
|
|
Expanded(
|
|
flex: 2,
|
|
child: Container(
|
|
decoration: BoxDecoration(
|
|
color: Color(0xFFE6F0FA),
|
|
// color: Color(0xFFF0F7FF),
|
|
// color: Colors.white,
|
|
borderRadius: BorderRadius.only(
|
|
topRight: Radius.circular(250), // Rounded top-left corner
|
|
bottomRight: Radius.circular(
|
|
250,
|
|
), // Rounded bottom-left corner
|
|
),
|
|
),
|
|
// child: Padding(
|
|
// padding: const EdgeInsets.only(
|
|
// left: 0,
|
|
// ),
|
|
// child: Align(
|
|
// alignment: Alignment.topLeft,
|
|
// child: Image.asset(
|
|
// 'assets/images/login/logoNew.jpg',
|
|
// width: 200, // Optional: control size
|
|
// height: 100,
|
|
// fit: BoxFit.contain,
|
|
// )),
|
|
// ),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
// Image.asset(
|
|
// 'assets/images/login/logoNew.jpg',
|
|
// width: 200, // Optional: control size
|
|
// height: 100,
|
|
// fit: BoxFit.contain,
|
|
// ),
|
|
Container(
|
|
width: 200, // Optional: control size
|
|
height: 100,
|
|
),
|
|
Expanded(
|
|
child: Container(
|
|
child: Align(
|
|
// alignment: Alignment.bottomRight,
|
|
child: Image.asset(
|
|
'assets/images/login/login_travel.png',
|
|
// width: 200, // Optional: control size
|
|
// height: 100,
|
|
fit: BoxFit.contain,
|
|
),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
Expanded(
|
|
flex: 1,
|
|
child: Container(
|
|
decoration: BoxDecoration(
|
|
// color: Colors.white,
|
|
borderRadius: BorderRadius.only(
|
|
topLeft: Radius.circular(25), // Rounded top-left corner
|
|
bottomLeft: Radius.circular(25), // Rounded bottom-left corner
|
|
),
|
|
),
|
|
|
|
child: Padding(
|
|
padding: const EdgeInsets.all(10),
|
|
child: _buildForm(width: formWidth), // Fixed form width
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
/// **Reusable Login Form**
|
|
Widget _buildForm({required double width}) {
|
|
return Column(
|
|
children: [
|
|
// width: width,
|
|
// Row(
|
|
// mainAxisAlignment: MainAxisAlignment.end,
|
|
// children: [
|
|
//
|
|
// ],
|
|
// ),
|
|
Expanded(
|
|
child: Column(
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
children: [
|
|
Row(
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
crossAxisAlignment: CrossAxisAlignment.center,
|
|
children: [
|
|
SizedBox(
|
|
width: width,
|
|
child: Form(
|
|
key: _formKey,
|
|
child: Column(
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
Image.asset(
|
|
'assets/images/login/logoNew.jpg',
|
|
width: 180, // Optional: control size
|
|
height: 70,
|
|
fit: BoxFit.contain,
|
|
),
|
|
const SizedBox(height: 2),
|
|
Text(
|
|
"Sign In",
|
|
style: GoogleFonts.poppins(
|
|
fontSize: widget.isDesktop ? 20 : 18,
|
|
fontWeight: FontWeight.w700,
|
|
color: Colors.green,
|
|
// color: Color(0xFF212121),
|
|
),
|
|
),
|
|
const SizedBox(height: 5),
|
|
Text(
|
|
"Welcome To TripApprovalTool",
|
|
style: GoogleFonts.poppins(
|
|
fontSize: 11,
|
|
fontWeight: FontWeight.w400,
|
|
|
|
color: Color(0xFF212121),
|
|
),
|
|
),
|
|
const SizedBox(height: 10),
|
|
|
|
/// **Email Field**
|
|
if (!_isForgotPassword && !_showOtpResetFields) ...[
|
|
_buildLabel("Email Address"),
|
|
TextFormField(
|
|
controller: _emailController,
|
|
style: GoogleFonts.poppins(
|
|
fontWeight: FontWeight.w600,
|
|
fontSize: 11,
|
|
),
|
|
decoration: _inputDecoration(
|
|
"Enter your email address",
|
|
).copyWith(
|
|
prefixIcon: Icon(
|
|
Icons.email_outlined,
|
|
size: 16,
|
|
),
|
|
),
|
|
validator:
|
|
(value) =>
|
|
value == null || value.isEmpty
|
|
? 'Required Email'
|
|
: null,
|
|
),
|
|
|
|
const SizedBox(height: 10),
|
|
|
|
/// **Password Field**
|
|
_buildLabel("Password"),
|
|
// TextFormField(
|
|
// controller: _passwordController,
|
|
// style: GoogleFonts.poppins(
|
|
// fontWeight: FontWeight.w600,
|
|
// fontSize: 11,
|
|
// ),
|
|
// obscureText: _obscureText,
|
|
//
|
|
// decoration: _inputDecoration(
|
|
// "Enter your password",
|
|
// ).copyWith(
|
|
// prefixIcon: Icon(Icons.key, size: 16),
|
|
// suffixIcon: IconButton(
|
|
// icon: Icon(
|
|
// _obscureText
|
|
// ? Icons.visibility_off
|
|
// : Icons.visibility,
|
|
// color: Color(0xFF12B24B),
|
|
// size: 16,
|
|
// ),
|
|
//
|
|
// onPressed:
|
|
// () => setState(
|
|
// () => _obscureText = !_obscureText,
|
|
// ),
|
|
// ),
|
|
// ),
|
|
//
|
|
// validator:
|
|
// (value) =>
|
|
// value == null || value.isEmpty
|
|
// ? 'Required Password'
|
|
// : null,
|
|
// ),
|
|
TextFormField(
|
|
controller: _passwordController,
|
|
style: GoogleFonts.poppins(
|
|
fontWeight: FontWeight.w600,
|
|
fontSize: 11,
|
|
),
|
|
obscureText: _obscureText,
|
|
textInputAction: TextInputAction.done,
|
|
decoration: _inputDecoration(
|
|
"Enter your password",
|
|
).copyWith(
|
|
prefixIcon: Icon(Icons.key, size: 16),
|
|
suffixIcon: IconButton(
|
|
icon: Icon(
|
|
_obscureText
|
|
? Icons.visibility_off
|
|
: Icons.visibility,
|
|
color: Color(0xFF12B24B),
|
|
size: 16,
|
|
),
|
|
onPressed:
|
|
() => setState(
|
|
() => _obscureText = !_obscureText,
|
|
),
|
|
),
|
|
),
|
|
onFieldSubmitted: (_) {
|
|
if (_formKey.currentState!.validate()) {
|
|
_login(context);
|
|
}
|
|
},
|
|
validator:
|
|
(value) =>
|
|
value == null || value.isEmpty
|
|
? 'Required Password'
|
|
: null,
|
|
),
|
|
const SizedBox(height: 10),
|
|
|
|
/// **Login Button**
|
|
Row(
|
|
children: [
|
|
Expanded(
|
|
child: ElevatedButton(
|
|
onPressed: () => _login(context),
|
|
style: ElevatedButton.styleFrom(
|
|
backgroundColor: Color(
|
|
0xFF12B24B,
|
|
), // Button color
|
|
foregroundColor:
|
|
Colors.white, // Text color
|
|
padding: const EdgeInsets.symmetric(
|
|
horizontal: 24,
|
|
vertical: 12,
|
|
),
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(18),
|
|
),
|
|
),
|
|
child: Padding(
|
|
padding: const EdgeInsets.symmetric(
|
|
horizontal: 24,
|
|
vertical: 5,
|
|
),
|
|
child: Row(
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
Text(
|
|
"Sign In",
|
|
style: GoogleFonts.poppins(
|
|
fontWeight: FontWeight.w800,
|
|
fontSize: 13.5,
|
|
),
|
|
),
|
|
SizedBox(width: 3),
|
|
Icon(
|
|
Icons.arrow_forward_sharp,
|
|
color: Colors.white,
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
] else if (_isForgotPassword &&
|
|
!_showOtpResetFields) ...[
|
|
_buildLabel("Email Address"),
|
|
TextFormField(
|
|
controller: _emailController,
|
|
style: GoogleFonts.poppins(
|
|
fontWeight: FontWeight.w600,
|
|
fontSize: 11,
|
|
),
|
|
decoration: _inputDecoration(
|
|
"Enter your email address",
|
|
).copyWith(
|
|
prefixIcon: Icon(
|
|
Icons.email_outlined,
|
|
size: 16,
|
|
),
|
|
),
|
|
validator:
|
|
(value) =>
|
|
value == null || value.isEmpty
|
|
? 'Required Email'
|
|
: null,
|
|
),
|
|
const SizedBox(height: 10),
|
|
Row(
|
|
children: [
|
|
Expanded(
|
|
child: ElevatedButton(
|
|
onPressed: () => _onSubmit(context),
|
|
style: ElevatedButton.styleFrom(
|
|
backgroundColor: Color(
|
|
0xFF12B24B,
|
|
), // Button color
|
|
foregroundColor:
|
|
Colors.white, // Text color
|
|
padding: const EdgeInsets.symmetric(
|
|
horizontal: 24,
|
|
vertical: 12,
|
|
),
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(18),
|
|
),
|
|
),
|
|
child: Padding(
|
|
padding: const EdgeInsets.symmetric(
|
|
horizontal: 24,
|
|
vertical: 5,
|
|
),
|
|
child: Row(
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
Text(
|
|
"Submit",
|
|
style: GoogleFonts.poppins(
|
|
fontWeight: FontWeight.w800,
|
|
fontSize: 13.5,
|
|
),
|
|
),
|
|
SizedBox(width: 3),
|
|
Icon(
|
|
Icons.arrow_forward_sharp,
|
|
color: Colors.white,
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
] else ...[
|
|
_buildLabel("Email Address"),
|
|
TextFormField(
|
|
controller: _emailController,
|
|
readOnly: true,
|
|
style: GoogleFonts.poppins(
|
|
fontWeight: FontWeight.w600,
|
|
fontSize: 11,
|
|
),
|
|
decoration: _inputDecoration(
|
|
"Enter your email address",
|
|
).copyWith(
|
|
prefixIcon: Icon(
|
|
Icons.email_outlined,
|
|
size: 16,
|
|
),
|
|
),
|
|
validator:
|
|
(value) =>
|
|
value == null || value.isEmpty
|
|
? 'Required Email'
|
|
: null,
|
|
),
|
|
const SizedBox(height: 10),
|
|
_buildLabel("OTP"),
|
|
TextFormField(
|
|
controller: _otpController,
|
|
style: GoogleFonts.poppins(
|
|
fontWeight: FontWeight.w600,
|
|
fontSize: 11,
|
|
),
|
|
decoration: _inputDecoration(
|
|
"Enter your OTP",
|
|
).copyWith(
|
|
prefixIcon: Icon(
|
|
Icons.email_outlined,
|
|
size: 16,
|
|
),
|
|
),
|
|
validator:
|
|
(value) =>
|
|
value == null || value.isEmpty
|
|
? 'Required OTP'
|
|
: null,
|
|
),
|
|
const SizedBox(height: 10),
|
|
_buildLabel("New Password"),
|
|
TextFormField(
|
|
controller: _newPasswordController,
|
|
style: GoogleFonts.poppins(
|
|
fontWeight: FontWeight.w600,
|
|
fontSize: 11,
|
|
),
|
|
obscureText: _obscureText,
|
|
decoration: _inputDecoration(
|
|
"Enter your new password",
|
|
).copyWith(
|
|
prefixIcon: Icon(Icons.key, size: 16),
|
|
suffixIcon: IconButton(
|
|
icon: Icon(
|
|
_obscureText
|
|
? Icons.visibility_off
|
|
: Icons.visibility,
|
|
color: Color(0xFF12B24B),
|
|
size: 16,
|
|
),
|
|
onPressed:
|
|
() => setState(
|
|
() => _obscureText = !_obscureText,
|
|
),
|
|
),
|
|
),
|
|
validator:
|
|
(value) =>
|
|
value == null || value.isEmpty
|
|
? 'Required New Password'
|
|
: null,
|
|
),
|
|
const SizedBox(height: 10),
|
|
_buildLabel("Confirm Password"),
|
|
TextFormField(
|
|
controller: _confirmPasswordController,
|
|
style: GoogleFonts.poppins(
|
|
fontWeight: FontWeight.w600,
|
|
fontSize: 11,
|
|
),
|
|
obscureText: _obscureText,
|
|
decoration: _inputDecoration(
|
|
"Enter your confirm password",
|
|
).copyWith(
|
|
prefixIcon: Icon(Icons.key, size: 16),
|
|
suffixIcon: IconButton(
|
|
icon: Icon(
|
|
_obscureText
|
|
? Icons.visibility_off
|
|
: Icons.visibility,
|
|
color: Color(0xFF12B24B),
|
|
size: 16,
|
|
),
|
|
onPressed:
|
|
() => setState(
|
|
() => _obscureText = !_obscureText,
|
|
),
|
|
),
|
|
),
|
|
validator: (value) {
|
|
if (value == null || value.isEmpty) {
|
|
return 'Required New Password';
|
|
}
|
|
if (value != _newPasswordController.text) {
|
|
return 'Passwords do not match';
|
|
}
|
|
return null;
|
|
},
|
|
),
|
|
const SizedBox(height: 10),
|
|
Row(
|
|
children: [
|
|
Expanded(
|
|
child: ElevatedButton(
|
|
onPressed: () {
|
|
if (!_formKey.currentState!.validate())
|
|
return;
|
|
setState(() {
|
|
_isForgotPassword = false;
|
|
_showOtpResetFields = true;
|
|
_passwordController.clear();
|
|
});
|
|
_onSubmit(context);
|
|
},
|
|
style: ElevatedButton.styleFrom(
|
|
backgroundColor: Color(
|
|
0xFF12B24B,
|
|
), // Button color
|
|
foregroundColor:
|
|
Colors.white, // Text color
|
|
padding: const EdgeInsets.symmetric(
|
|
horizontal: 24,
|
|
vertical: 12,
|
|
),
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(18),
|
|
),
|
|
),
|
|
child: Padding(
|
|
padding: const EdgeInsets.symmetric(
|
|
horizontal: 24,
|
|
vertical: 10,
|
|
),
|
|
child: Row(
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
Text(
|
|
"Submit",
|
|
style: GoogleFonts.poppins(
|
|
fontWeight: FontWeight.w800,
|
|
fontSize: 13.5,
|
|
),
|
|
),
|
|
SizedBox(width: 3),
|
|
Icon(
|
|
Icons.arrow_forward_sharp,
|
|
color: Colors.white,
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
],
|
|
const SizedBox(height: 10),
|
|
if (!_isForgotPassword && !_showOtpResetFields)
|
|
Center(
|
|
child: TextButton(
|
|
onPressed: () {
|
|
setState(() {
|
|
_isForgotPassword = true;
|
|
_showOtpResetFields = false;
|
|
_passwordController.clear();
|
|
});
|
|
},
|
|
child: Text(
|
|
"Forgot Password",
|
|
style: GoogleFonts.poppins(
|
|
fontSize: 11,
|
|
fontWeight: FontWeight.w300,
|
|
|
|
color: Color(0xFF212121), // Text color
|
|
decoration:
|
|
TextDecoration
|
|
.underline, // Underline the text
|
|
),
|
|
),
|
|
),
|
|
),
|
|
if (_isForgotPassword || _showOtpResetFields)
|
|
Center(
|
|
child: TextButton(
|
|
onPressed: () {
|
|
setState(() {
|
|
_isForgotPassword = false;
|
|
_showOtpResetFields = false;
|
|
_clearAllFields();
|
|
});
|
|
},
|
|
child: Text(
|
|
"Back to Login",
|
|
style: GoogleFonts.poppins(
|
|
fontSize: 11,
|
|
fontWeight: FontWeight.w300,
|
|
|
|
color: Color(0xFF212121), // Text color
|
|
decoration:
|
|
TextDecoration
|
|
.underline, // Underline the text
|
|
),
|
|
),
|
|
),
|
|
),
|
|
|
|
const SizedBox(height: 10),
|
|
if (!_isForgotPassword && !_showOtpResetFields)
|
|
Row(
|
|
children: [
|
|
Expanded(
|
|
child: ElevatedButton(
|
|
onPressed: () {
|
|
handleMS();
|
|
},
|
|
style: ElevatedButton.styleFrom(
|
|
backgroundColor:
|
|
Colors.white, // Button color
|
|
foregroundColor:
|
|
Colors.black, // Text color
|
|
padding: const EdgeInsets.symmetric(
|
|
horizontal: 24,
|
|
vertical: 5,
|
|
),
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(18),
|
|
side: BorderSide(
|
|
color: Colors.black, // Border color
|
|
width: 0.5, // Border width
|
|
),
|
|
),
|
|
),
|
|
child: Padding(
|
|
padding: const EdgeInsets.symmetric(
|
|
horizontal: 24,
|
|
vertical: 3,
|
|
),
|
|
child: Row(
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
Text(
|
|
"Sign In With Microsoft",
|
|
style: GoogleFonts.poppins(
|
|
fontWeight: FontWeight.w500,
|
|
fontSize: 13,
|
|
),
|
|
),
|
|
SizedBox(width: 3),
|
|
Image.asset(
|
|
'assets/images/login/microsoft.png',
|
|
width: 30, // Optional: control size
|
|
height: 30,
|
|
fit: BoxFit.contain,
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
// Row(
|
|
// mainAxisAlignment: MainAxisAlignment.center,
|
|
// children: [
|
|
// Container(
|
|
// width: 30, // Adjust size
|
|
// height: 30,
|
|
// decoration: BoxDecoration(
|
|
// // Background color
|
|
// shape: BoxShape.rectangle,
|
|
// borderRadius: BorderRadius.all(Radius.circular(15)),
|
|
// border: Border.all(
|
|
// color: Color(0xFF9E9DBD), width: 1), // Grey outline
|
|
// ),
|
|
//
|
|
// child: Center(
|
|
// child: Image.asset(
|
|
// 'assets/images/login/VectorG.png',
|
|
// width: 20, // Optional: control size
|
|
// height: 20,
|
|
// fit: BoxFit.contain,
|
|
// )),
|
|
// ),
|
|
// ],
|
|
// ),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
],
|
|
),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
|
|
/// **Reusable Label Widget**
|
|
Widget _buildLabel(String text) {
|
|
return Align(
|
|
alignment: Alignment.centerLeft,
|
|
child: Padding(
|
|
padding: const EdgeInsets.only(bottom: 8),
|
|
child: Text(
|
|
text,
|
|
style: GoogleFonts.poppins(
|
|
fontSize: 12,
|
|
fontWeight: FontWeight.w600,
|
|
color: Color(0xFF212121),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
/// **Reusable Input Decoration**
|
|
InputDecoration _inputDecoration(String hint) {
|
|
return InputDecoration(
|
|
labelText: hint,
|
|
floatingLabelBehavior: FloatingLabelBehavior.never,
|
|
contentPadding: EdgeInsets.symmetric(vertical: 1.0, horizontal: 0.0),
|
|
filled: true,
|
|
fillColor: Colors.white,
|
|
labelStyle: GoogleFonts.poppins(fontSize: 11, color: Colors.grey),
|
|
border: OutlineInputBorder(
|
|
borderRadius: BorderRadius.circular(18),
|
|
borderSide: BorderSide(
|
|
color: Color(0xFF212121), // Border color
|
|
width: 1, // Optional: adjust the width of the border
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
Future<void> handleMS() async {
|
|
final url = '$apiUrl/auth/mslogin';
|
|
print(url);
|
|
try {
|
|
final response = await http.get(
|
|
Uri.parse(url),
|
|
headers: {'Content-Type': 'application/json'},
|
|
);
|
|
print("inside try method");
|
|
if (response.statusCode == 200) {
|
|
final authUrl = json.decode(response.body)['auth_url'];
|
|
print("authurl - $authUrl");
|
|
if (authUrl != '') {
|
|
// final prefs = await SharedPreferences.getInstance();
|
|
// await prefs.setString('auth_token', authUrl);
|
|
print('i have auth URL');
|
|
// canLaunchUrl(authUrl);
|
|
|
|
if (kIsWeb) {
|
|
print("kIsWeb");
|
|
// Use web redirect (e.g., via JS interop or window.location.href)
|
|
// redirectTo(url);
|
|
|
|
html.window.location.href = authUrl;
|
|
} else {
|
|
// For mobile/desktop, open in external browser
|
|
// launchUrl(Uri.parse(url),
|
|
// mode: LaunchMode.externalApplication);
|
|
}
|
|
// final result = await FlutterWebAuth.authenticate(
|
|
// url: authUrl,
|
|
// callbackUrlScheme: "myapp", // Use a custom scheme you registered
|
|
// );
|
|
} else {
|
|
print('auth URL not Founded');
|
|
throw Exception('auth URL not Founded');
|
|
}
|
|
} else {
|
|
final errorMessage = json.decode(response.body)['message'];
|
|
print(errorMessage);
|
|
throw Exception(errorMessage);
|
|
}
|
|
} catch (e) {
|
|
print("Error: $e");
|
|
}
|
|
}
|
|
}
|