Commit 5b790690 authored by Ilham Maulana's avatar Ilham Maulana 💻

feat: reset pasword confirm by pin sended with email

parent 99c75c17
......@@ -5,7 +5,6 @@ import 'package:http/http.dart' as http;
import 'package:library_app/src/models/token.dart';
import 'package:library_app/src/models/user.dart';
// Flutter: make memberLoans adjustable to be filtered to near outstanding loan and overdued loan
class AuthProvider with ChangeNotifier {
String baseUrl = 'http://localhost:8000/api/v1';
......@@ -18,6 +17,10 @@ class AuthProvider with ChangeNotifier {
bool filterByUpcoming = false;
bool filterByOverdued = false;
int? userIdResetPw;
bool resetPasswordTokenSended = false;
bool resetPasswordSucced = false;
Future<void> signIn(String username, String password) async {
try {
final response = await http.post(
......@@ -161,6 +164,57 @@ class AuthProvider with ChangeNotifier {
}
}
Future<void> resetPassword(String email) async {
try {
final response = await http.post(
Uri.parse('$baseUrl/reset-password/request-token'),
body: jsonEncode({"email": email}),
headers: {'Content-Type': 'application/json'},
);
if (response.statusCode == 200) {
final data = jsonDecode(response.body);
resetPasswordTokenSended = true;
userIdResetPw = data["user_id_reset_pw"];
} else {
debugPrint(
'Error reset user password: ${response.statusCode}, ${response.body}');
}
notifyListeners();
} catch (error) {
debugPrint("Error reset user password: $error");
}
}
Future<void> confirmResetPassword(
int pin, String password1, String password2) async {
final body = jsonEncode({
"pin": pin,
"password1": password1,
"password2": password2,
});
try {
final response = await http.post(
Uri.parse('$baseUrl/reset-password/confirm'),
body: body,
headers: {'Content-Type': 'application/json'},
);
if (response.statusCode == 200) {
resetPasswordSucced = true;
} else {
debugPrint(
'Error confirm reset user password: ${response.statusCode}, ${response.body}');
}
notifyListeners();
} catch (error) {
debugPrint("Error confirm reset user password: $error");
}
}
Future<void> getMemberLoan() async {
String url = '$baseUrl/members/${user?.accountId}/loans/';
if (filterByUpcoming) {
......
......@@ -75,6 +75,20 @@ class ResetPasswordScreen extends StatelessWidget {
}
}
class ConfirmResetPasswordScreen extends StatelessWidget {
const ConfirmResetPasswordScreen({super.key});
@override
Widget build(BuildContext context) {
String title = "Reset Password";
return FormScreen(
title: title,
body: const ConfirmResetPasswordForm(),
);
}
}
class ProfileEditScreen extends StatelessWidget {
const ProfileEditScreen({
super.key,
......
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:library_app/src/providers/auth_provider.dart';
import 'package:library_app/src/screens/form_screen.dart';
import 'package:provider/provider.dart';
class ResetPasswordForm extends StatefulWidget {
const ResetPasswordForm({super.key});
......@@ -10,93 +13,276 @@ class ResetPasswordForm extends StatefulWidget {
class _ResetPasswordForm extends State<ResetPasswordForm> {
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
bool passwordVisible = false;
@override
void initState() {
super.initState();
passwordVisible = true;
}
final emailController = TextEditingController();
@override
Widget build(BuildContext context) {
const String title = "Reset Password";
const String formText = "Confirm your email to continue reset password";
return Column(
children: [
Container(
padding: const EdgeInsets.symmetric(horizontal: 20.0, vertical: 20.0),
child: Column(
children: [
SvgPicture.asset(
"assets/images/reset_password_image.svg",
semanticsLabel: title,
width: 200,
),
const SizedBox(
height: 10.0,
return Consumer<AuthProvider>(builder: (context, authProvider, child) {
return Column(
children: [
Container(
padding:
const EdgeInsets.symmetric(horizontal: 20.0, vertical: 20.0),
child: Column(
children: [
SvgPicture.asset(
"assets/images/reset_password_image.svg",
semanticsLabel: title,
width: 200,
),
const SizedBox(
height: 10.0,
),
const Text(
formText,
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
textAlign: TextAlign.center,
),
],
),
),
Form(
key: _formKey,
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 30.0,
vertical: 30.0,
),
const Text(
formText,
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
textAlign: TextAlign.center,
child: Container(
padding: const EdgeInsets.all(10.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
TextFormField(
controller: emailController,
decoration: const InputDecoration(
hintText: "Enter your Email",
labelText: "Email",
suffixIcon: Icon(Icons.email_rounded),
),
validator: (String? value) {
if (value == null || value.isEmpty) {
return "Please enter your email";
} else if (!value.contains("@")) {
return "Email should include '@'";
}
return null;
},
),
// Flutter, iwant to go to ConfirmResetPasswordScreen after authProvider.resetPassword succeed with response 200
Container(
padding: const EdgeInsets.symmetric(
vertical: 20.0,
),
child: Column(
children: [
SizedBox(
width: double.infinity,
child: FilledButton(
onPressed: () {
if (_formKey.currentState!.validate()) {}
authProvider
.resetPassword(emailController.text)
.then(
(response) {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) =>
const ConfirmResetPasswordScreen(),
),
);
},
).catchError(
(error) {
debugPrint('Exception: $error');
},
);
},
child: const Text("Submit"),
),
),
],
),
)
],
),
),
],
),
),
Form(
key: _formKey,
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 30.0,
vertical: 30.0,
),
child: Container(
padding: const EdgeInsets.all(10.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
TextFormField(
decoration: const InputDecoration(
hintText: "Enter your Email",
labelText: "Email",
suffixIcon: Icon(Icons.email_rounded),
)
],
);
});
}
}
class ConfirmResetPasswordForm extends StatefulWidget {
const ConfirmResetPasswordForm({super.key});
@override
State<ConfirmResetPasswordForm> createState() => _ConfirmResetPasswordForm();
}
class _ConfirmResetPasswordForm extends State<ConfirmResetPasswordForm> {
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
final pinController = TextEditingController();
final password1Controller = TextEditingController();
final password2Controller = TextEditingController();
bool passwordVisible = false;
@override
Widget build(BuildContext context) {
const String title = "Confirm Reset Password Pin";
const String formText = "Enter the pin that we just sent to your email";
return Consumer<AuthProvider>(builder: (context, authProvider, child) {
return Column(
children: [
Container(
padding:
const EdgeInsets.symmetric(horizontal: 20.0, vertical: 20.0),
child: Column(
children: [
SvgPicture.asset(
"assets/images/reset_password_image.svg",
semanticsLabel: title,
width: 200,
),
const SizedBox(
height: 10.0,
),
const Text(
formText,
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
textAlign: TextAlign.center,
),
],
),
),
Form(
key: _formKey,
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 30.0,
vertical: 30.0,
),
child: Container(
padding: const EdgeInsets.all(10.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
TextFormField(
controller: pinController,
decoration: const InputDecoration(
hintText: "Enter your confirmation pin",
labelText: "confirmation pin",
suffixIcon: Icon(Icons.password),
),
),
validator: (String? value) {
if (value == null || value.isEmpty) {
return "Please enter your email";
} else if (!value.contains("@")) {
return "Email should include '@'";
}
return null;
},
),
Container(
padding: const EdgeInsets.symmetric(
vertical: 20.0,
TextFormField(
controller: password1Controller,
obscureText: passwordVisible,
decoration: InputDecoration(
hintText: "Enter your new Password",
labelText: "New Password",
suffixIcon: IconButton(
icon: Icon(passwordVisible
? Icons.visibility
: Icons.visibility_off),
onPressed: () {
setState(
() {
passwordVisible = !passwordVisible;
},
);
},
),
),
validator: (String? value) {
if (value == null || value.isEmpty) {
return "Please enter your password";
} else {
return null;
}
},
keyboardType: TextInputType.visiblePassword,
),
child: Column(
children: [
SizedBox(
width: double.infinity,
child: FilledButton(
onPressed: () {
if (_formKey.currentState!.validate()) {}
},
child: const Text("Submit"),
),
TextFormField(
controller: password2Controller,
obscureText: passwordVisible,
decoration: InputDecoration(
hintText: "Confirm your new Password",
labelText: "Confirm new Password",
suffixIcon: IconButton(
icon: Icon(passwordVisible
? Icons.visibility
: Icons.visibility_off),
onPressed: () {
setState(
() {
passwordVisible = !passwordVisible;
},
);
},
),
],
),
validator: (String? value) {
if (value == null || value.isEmpty) {
return "Please enter your password";
} else {
return null;
}
},
keyboardType: TextInputType.visiblePassword,
),
)
],
Container(
padding: const EdgeInsets.symmetric(
vertical: 20.0,
),
child: Column(
children: [
SizedBox(
width: double.infinity,
child: FilledButton(
onPressed: () {
if (_formKey.currentState!.validate()) {}
authProvider
.confirmResetPassword(
int.parse(pinController.text),
password1Controller.text,
password2Controller.text,
)
.then(
(response) {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) =>
const LoginScreen(),
),
);
},
).catchError(
(error) {
debugPrint('Exception: $error');
},
);
},
child: const Text("Submit"),
),
),
],
),
)
],
),
),
),
),
)
],
);
)
],
);
});
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment