diff --git a/Frontend/patient_manager/lib/pages/authentication/forgotPassword.dart b/Frontend/patient_manager/lib/pages/authentication/forgotPassword.dart new file mode 100644 index 00000000..70756655 --- /dev/null +++ b/Frontend/patient_manager/lib/pages/authentication/forgotPassword.dart @@ -0,0 +1,337 @@ +import 'dart:convert'; + +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:patient_manager/components/popUpMessages/mihErrorMessage.dart'; +import 'package:patient_manager/components/inputsAndButtons/mihTextInput.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 ForgotPassword extends StatefulWidget { + const ForgotPassword({super.key}); + + @override + State createState() => _ForgotPasswordState(); +} + +class _ForgotPasswordState extends State { + final emailController = TextEditingController(); + + //bool _obscureText = true; + bool successfulForgotPassword = 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 response = await http.post( + Uri.parse("$baseAPI/auth/user/password/reset/token"), + body: + '{"formFields": [{"id": "email","value": "${emailController.text}"}]}', + headers: { + 'Content-type': 'application/json', + 'Accept': 'application/json', + //"Authorization": "leatucczyixqwkqqdrhayiwzeofkltds" + }, + ); + //print(response.body[]) + if (response.statusCode == 200) { + //print(response.body); + var userSignedin = jsonDecode(response.body); + if (userSignedin["status"] == "OK") { + //print("here"); + setState(() { + successfulForgotPassword = true; + }); + Navigator.of(context).pop(); + } else { + Navigator.of(context).pop(); + //loginError(); + } + } + } on AuthException { + Navigator.of(context).pop(); + //loginError(); + } + } + + 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 resetLinkSentSuccessfully() { + showDialog( + context: context, + builder: (context) { + return const MIHSuccessMessage( + successType: "Success", + successMessage: + "We've sent a password reset link to your email address. Please check your inbox, including spam or junk folders.\n\nOnce you find the email, click on the link to reset your password.\n\nIf you don't receive the email within a few minutes, please try resending the reset request.\n\nThe reset link will expire after 2 hours"); + }, + ); + } + + void validateInput() async { + if (emailController.text.isEmpty) { + showDialog( + context: context, + builder: (context) { + return const MIHErrorMessage(errorType: "Input Error"); + }, + ); + } else { + await submitPasswodReset(); + if (successfulForgotPassword) { + Navigator.of(context).pushNamedAndRemoveUntil('/', (route) => false); + resetLinkSentSuccessfully(); + } + } + } + + @override + void dispose() { + emailController.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( + 'Forgot Password', + style: TextStyle( + fontSize: 25, + fontWeight: FontWeight.bold, + color: MzanziInnovationHub.of(context)! + .theme + .secondaryColor(), + ), + ), + //spacer + const SizedBox(height: 25), + + //email input + SizedBox( + width: 500.0, + child: MIHTextField( + controller: emailController, + hintText: 'Email', + editable: true, + required: true, + ), + ), + + //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: () { + prePassResteWarning(); + }, + ), + ), + ], + ), + ), + ), + ), + ), + Positioned( + top: 10, + left: 5, + width: 50, + height: 50, + child: IconButton( + onPressed: () { + Navigator.of(context).pop(); + }, + icon: const Icon(Icons.arrow_back), + ), + ), + ], + ), + ), + ); + } +}