From 9d766b44f23d3e0481644ea6d0444ea98eede45e Mon Sep 17 00:00:00 2001 From: yaso-meth Date: Mon, 2 Sep 2024 11:07:16 +0200 Subject: [PATCH] add reset password page that users navigate to from external link to capture new password --- .../pages/authentication/resetPassword.dart | 393 ++++++++++++++++++ 1 file changed, 393 insertions(+) create mode 100644 Frontend/patient_manager/lib/pages/authentication/resetPassword.dart diff --git a/Frontend/patient_manager/lib/pages/authentication/resetPassword.dart b/Frontend/patient_manager/lib/pages/authentication/resetPassword.dart new file mode 100644 index 00000000..3be0fc3b --- /dev/null +++ b/Frontend/patient_manager/lib/pages/authentication/resetPassword.dart @@ -0,0 +1,393 @@ +import 'dart:convert'; + +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:patient_manager/components/inputsAndButtons/mihPassInput.dart'; +import 'package:patient_manager/components/popUpMessages/mihErrorMessage.dart'; +import 'package:patient_manager/components/inputsAndButtons/mihButton.dart'; +import 'package:patient_manager/components/popUpMessages/mihLoadingCircle.dart'; +import 'package:patient_manager/components/popUpMessages/mihSuccessMessage.dart'; +import 'package:patient_manager/env/env.dart'; +import 'package:patient_manager/main.dart'; +import 'package:supabase_auth_ui/supabase_auth_ui.dart'; +import 'package:supertokens_flutter/http.dart' as http; + +class ResetPassword extends StatefulWidget { + final String? token; + const ResetPassword({ + super.key, + required this.token, + }); + + @override + State createState() => _ResetPasswordState(); +} + +class _ResetPasswordState extends State { + final passwordController = TextEditingController(); + final confirmPasswordController = TextEditingController(); + + //bool _obscureText = true; + bool successfulResetPassword = false; + bool acceptWarning = false; + // focus node to capture keyboard events + final FocusNode _focusNode = FocusNode(); + + final baseAPI = AppEnviroment.baseApiUrl; + + Future submitPasswodReset() async { + showDialog( + context: context, + builder: (context) { + return const Mihloadingcircle(); + }, + ); + + try { + var callBody = + '{"method": "token","formFields": [{"id": "password","value": "${passwordController.text}"}],"token": "${widget.token}"}'; + //print(callBody); + var response = await http.post( + Uri.parse("$baseAPI/auth/user/password/reset"), + body: callBody, + headers: { + 'Content-type': 'application/json; charset=utf-8', + // 'Accept': 'application/json', + //"Authorization": "leatucczyixqwkqqdrhayiwzeofkltds" + }, + ); + //print(response.body[]) + if (response.statusCode == 200) { + //print(response.body); + var userPassReset = jsonDecode(response.body); + if (userPassReset["status"] == "OK") { + //print("here"); + setState(() { + successfulResetPassword = true; + }); + Navigator.of(context).pop(); + Navigator.of(context).pushNamed('/'); + } else if (userPassReset["status"] == "FIELD_ERROR") { + Navigator.of(context).pop(); + passwordReqError(); + } else { + Navigator.of(context).pop(); + loginError(); + } + } + } on AuthException { + Navigator.of(context).pop(); + //loginError(); + } + } + + void passwordReqError() { + showDialog( + context: context, + builder: (context) { + return const MIHErrorMessage(errorType: "Password Requirements"); + }, + ); + } + + Color getPrim() { + return MzanziInnovationHub.of(context)!.theme.secondaryColor(); + } + + Color getSec() { + return MzanziInnovationHub.of(context)!.theme.primaryColor(); + } + + void prePassResteWarning() { + showDialog( + context: context, + builder: (context) { + return Dialog( + child: Stack( + children: [ + Container( + padding: const EdgeInsets.all(10.0), + width: 500.0, + height: 450, + decoration: BoxDecoration( + color: MzanziInnovationHub.of(context)!.theme.primaryColor(), + borderRadius: BorderRadius.circular(25.0), + border: Border.all( + color: MzanziInnovationHub.of(context)! + .theme + .secondaryColor(), + width: 5.0), + ), + child: SingleChildScrollView( + child: Column( + //mainAxisSize: MainAxisSize.max, + children: [ + Icon( + Icons.warning_amber_rounded, + size: 100, + color: MzanziInnovationHub.of(context)! + .theme + .secondaryColor(), + ), + const SizedBox(height: 15), + Text( + "Password Reset Confirmation", + textAlign: TextAlign.center, + style: TextStyle( + color: MzanziInnovationHub.of(context)! + .theme + .secondaryColor(), + fontSize: 25.0, + fontWeight: FontWeight.bold, + ), + ), + const SizedBox(height: 15), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 25.0), + child: Text( + "Before you reset your password, please be aware that you'll receive an email with a link to confirm your identity and set a new password. Make sure to check your inbox, including spam or junk folders. If you don't receive the email within a few minutes, please try resending the reset request.", + style: TextStyle( + color: MzanziInnovationHub.of(context)! + .theme + .secondaryColor(), + fontSize: 15.0, + fontWeight: FontWeight.bold, + ), + ), + ), + const SizedBox(height: 25), + SizedBox( + width: 500.0, + height: 50.0, + child: MIHButton( + buttonText: "Continue", + buttonColor: MzanziInnovationHub.of(context)! + .theme + .secondaryColor(), + textColor: MzanziInnovationHub.of(context)! + .theme + .primaryColor(), + onTap: () { + setState(() { + acceptWarning = true; + }); + Navigator.of(context).pop(); + validateInput(); + }, + ), + ), + ], + ), + ), + ), + Positioned( + top: 5, + right: 5, + width: 50, + height: 50, + child: IconButton( + onPressed: () { + Navigator.pop(context); + }, + icon: Icon( + Icons.close, + color: MzanziInnovationHub.of(context)!.theme.errorColor(), + size: 35, + ), + ), + ), + ], + ), + ); + }, + ); + } + + void loginError() { + showDialog( + context: context, + builder: (context) { + return const MIHErrorMessage(errorType: "Invalid Credentials"); + }, + ); + } + + void resetSuccessfully() { + showDialog( + context: context, + builder: (context) { + return const MIHSuccessMessage( + successType: "Success", + successMessage: + "Great news! Your password reset is complete. You can now log in to Mzansi Innovation Hub using your new password."); + }, + ); + } + + void passwordError() { + showDialog( + context: context, + builder: (context) { + return const MIHErrorMessage(errorType: "Password Match"); + }, + ); + } + + void validateInput() async { + if (passwordController.text.isEmpty || + confirmPasswordController.text.isEmpty) { + showDialog( + context: context, + builder: (context) { + return const MIHErrorMessage(errorType: "Input Error"); + }, + ); + } else if (passwordController.text != confirmPasswordController.text) { + passwordError(); + } else { + await submitPasswodReset(); + if (successfulResetPassword) { + resetSuccessfully(); + } + } + } + + @override + void dispose() { + passwordController.dispose(); + confirmPasswordController.dispose(); + _focusNode.dispose(); + super.dispose(); + } + + @override + void initState() { + super.initState(); + } + + @override + Widget build(BuildContext context) { + return KeyboardListener( + focusNode: _focusNode, + autofocus: true, + onKeyEvent: (event) async { + if (event is KeyDownEvent && + event.logicalKey == LogicalKeyboardKey.enter) { + validateInput(); + } + }, + child: Scaffold( + //backgroundColor: Colors.white, + body: Stack( + children: [ + SafeArea( + child: Center( + child: SingleChildScrollView( + physics: const BouncingScrollPhysics(), + child: Padding( + padding: const EdgeInsets.all(25.0), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + //logo + Icon( + Icons.lock, + size: 100, + color: MzanziInnovationHub.of(context)! + .theme + .secondaryColor(), + ), + //spacer + const SizedBox(height: 10), + //Heading + Text( + 'Reset Password', + style: TextStyle( + fontSize: 25, + fontWeight: FontWeight.bold, + color: MzanziInnovationHub.of(context)! + .theme + .secondaryColor(), + ), + ), + //spacer + // const SizedBox(height: 15), + // Text( + // 'token: ${widget.token}', + // style: TextStyle( + // fontSize: 15, + // fontWeight: FontWeight.bold, + // color: MzanziInnovationHub.of(context)! + // .theme + // .secondaryColor(), + // ), + // ), + //spacer + const SizedBox(height: 25), + //email input + SizedBox( + width: 500.0, + child: MIHPassField( + controller: passwordController, + hintText: 'New Password', + required: true, + signIn: false, + ), + ), + //spacer + const SizedBox(height: 10), + //password input + SizedBox( + width: 500.0, + child: MIHPassField( + controller: confirmPasswordController, + hintText: 'Confirm New Password', + required: true, + signIn: false, + ), + ), + + //spacer + const SizedBox(height: 30), + // sign in button + SizedBox( + width: 500.0, + height: 50.0, + child: MIHButton( + buttonText: "Reset Password", + buttonColor: MzanziInnovationHub.of(context)! + .theme + .secondaryColor(), + textColor: MzanziInnovationHub.of(context)! + .theme + .primaryColor(), + onTap: () { + validateInput(); + }, + ), + ), + ], + ), + ), + ), + ), + ), + Positioned( + top: 10, + left: 5, + width: 50, + height: 50, + child: IconButton( + onPressed: () { + //Navigator.of(context).pop(); + Navigator.of(context).pushReplacementNamed('/'); + }, + icon: const Icon(Icons.login), + ), + ), + ], + ), + ), + ); + } +}