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; ...@@ -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/token.dart';
import 'package:library_app/src/models/user.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 { class AuthProvider with ChangeNotifier {
String baseUrl = 'http://localhost:8000/api/v1'; String baseUrl = 'http://localhost:8000/api/v1';
...@@ -18,6 +17,10 @@ class AuthProvider with ChangeNotifier { ...@@ -18,6 +17,10 @@ class AuthProvider with ChangeNotifier {
bool filterByUpcoming = false; bool filterByUpcoming = false;
bool filterByOverdued = false; bool filterByOverdued = false;
int? userIdResetPw;
bool resetPasswordTokenSended = false;
bool resetPasswordSucced = false;
Future<void> signIn(String username, String password) async { Future<void> signIn(String username, String password) async {
try { try {
final response = await http.post( final response = await http.post(
...@@ -161,6 +164,57 @@ class AuthProvider with ChangeNotifier { ...@@ -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 { Future<void> getMemberLoan() async {
String url = '$baseUrl/members/${user?.accountId}/loans/'; String url = '$baseUrl/members/${user?.accountId}/loans/';
if (filterByUpcoming) { if (filterByUpcoming) {
......
...@@ -75,6 +75,20 @@ class ResetPasswordScreen extends StatelessWidget { ...@@ -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 { class ProfileEditScreen extends StatelessWidget {
const ProfileEditScreen({ const ProfileEditScreen({
super.key, super.key,
......
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.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 { class ResetPasswordForm extends StatefulWidget {
const ResetPasswordForm({super.key}); const ResetPasswordForm({super.key});
...@@ -10,24 +13,19 @@ class ResetPasswordForm extends StatefulWidget { ...@@ -10,24 +13,19 @@ class ResetPasswordForm extends StatefulWidget {
class _ResetPasswordForm extends State<ResetPasswordForm> { class _ResetPasswordForm extends State<ResetPasswordForm> {
final GlobalKey<FormState> _formKey = GlobalKey<FormState>(); final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
final emailController = TextEditingController();
bool passwordVisible = false;
@override
void initState() {
super.initState();
passwordVisible = true;
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
const String title = "Reset Password"; const String title = "Reset Password";
const String formText = "Confirm your email to continue reset password"; const String formText = "Confirm your email to continue reset password";
return Consumer<AuthProvider>(builder: (context, authProvider, child) {
return Column( return Column(
children: [ children: [
Container( Container(
padding: const EdgeInsets.symmetric(horizontal: 20.0, vertical: 20.0), padding:
const EdgeInsets.symmetric(horizontal: 20.0, vertical: 20.0),
child: Column( child: Column(
children: [ children: [
SvgPicture.asset( SvgPicture.asset(
...@@ -59,6 +57,7 @@ class _ResetPasswordForm extends State<ResetPasswordForm> { ...@@ -59,6 +57,7 @@ class _ResetPasswordForm extends State<ResetPasswordForm> {
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[ children: <Widget>[
TextFormField( TextFormField(
controller: emailController,
decoration: const InputDecoration( decoration: const InputDecoration(
hintText: "Enter your Email", hintText: "Enter your Email",
labelText: "Email", labelText: "Email",
...@@ -73,6 +72,172 @@ class _ResetPasswordForm extends State<ResetPasswordForm> { ...@@ -73,6 +72,172 @@ class _ResetPasswordForm extends State<ResetPasswordForm> {
return null; 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"),
),
),
],
),
)
],
),
),
),
)
],
);
});
}
}
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),
),
),
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,
),
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( Container(
padding: const EdgeInsets.symmetric( padding: const EdgeInsets.symmetric(
vertical: 20.0, vertical: 20.0,
...@@ -84,6 +249,26 @@ class _ResetPasswordForm extends State<ResetPasswordForm> { ...@@ -84,6 +249,26 @@ class _ResetPasswordForm extends State<ResetPasswordForm> {
child: FilledButton( child: FilledButton(
onPressed: () { onPressed: () {
if (_formKey.currentState!.validate()) {} 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"), child: const Text("Submit"),
), ),
...@@ -98,5 +283,6 @@ class _ResetPasswordForm extends State<ResetPasswordForm> { ...@@ -98,5 +283,6 @@ class _ResetPasswordForm extends State<ResetPasswordForm> {
) )
], ],
); );
});
} }
} }
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