diff --git a/src/forms/registration-popup/messages.jsx b/src/forms/registration-popup/messages.jsx
index 9c590841..2965fb35 100644
--- a/src/forms/registration-popup/messages.jsx
+++ b/src/forms/registration-popup/messages.jsx
@@ -56,6 +56,16 @@ const messages = defineMessages({
defaultMessage: 'Fill out the fields below to create your account',
description: 'Heading that appear on the second step',
},
+ registrationFormPasswordFieldLabel: {
+ id: 'registration.form.password.label',
+ defaultMessage: 'Password',
+ description: 'Label for password input field',
+ },
+ registrationFormEmailFieldLabel: {
+ id: 'registration.form.email.label',
+ defaultMessage: 'Enter your email',
+ description: 'Label for email input field',
+ },
});
export default messages;
diff --git a/src/forms/reset-password-popup/ResetPasswordHeader.jsx b/src/forms/reset-password-popup/ResetPasswordHeader.jsx
new file mode 100644
index 00000000..8d5e38db
--- /dev/null
+++ b/src/forms/reset-password-popup/ResetPasswordHeader.jsx
@@ -0,0 +1,25 @@
+import React from 'react';
+
+import { useIntl } from '@edx/frontend-platform/i18n';
+
+import messages from './messages';
+
+/**
+ * Header component for the reset password form.
+ * Renders the heading for the reset password form along with a separator.
+ * @returns {JSX.Element} The rendered header component.
+ */
+const ResetPasswordHeader = () => {
+ const { formatMessage } = useIntl();
+
+ return (
+ <>
+
+ {formatMessage(messages.resetPasswordFormHeading)}
+
+
+ >
+ );
+};
+
+export default ResetPasswordHeader;
diff --git a/src/forms/reset-password-popup/forgot-password/ForgotPasswordEmailSentConfirmation.jsx b/src/forms/reset-password-popup/forgot-password/ForgotPasswordEmailSentConfirmation.jsx
new file mode 100644
index 00000000..e7387faa
--- /dev/null
+++ b/src/forms/reset-password-popup/forgot-password/ForgotPasswordEmailSentConfirmation.jsx
@@ -0,0 +1,45 @@
+import React from 'react';
+
+import { getConfig } from '@edx/frontend-platform';
+import { FormattedMessage, useIntl } from '@edx/frontend-platform/i18n';
+import { Alert } from '@openedx/paragon';
+
+import messages from '../messages';
+
+/**
+ * Component that renders the display confirmation message after sending the password reset email.
+ * @returns {JSX.Element} The rendered confirmation message component.
+ */
+const ForgotPasswordEmailSentConfirmation = () => {
+ const { formatMessage } = useIntl();
+
+ return (
+
+
+ {formatMessage(messages.emailSentMessage)}
+
+
+ email@email.com,
+ supportLink: (
+
+ {getConfig().INFO_EMAIL}
+
+ ),
+ }}
+ />
+
+
+
+ );
+};
+
+export default ForgotPasswordEmailSentConfirmation;
diff --git a/src/forms/reset-password-popup/forgot-password/ForgotPasswordPage.jsx b/src/forms/reset-password-popup/forgot-password/ForgotPasswordPage.jsx
new file mode 100644
index 00000000..5bd5483f
--- /dev/null
+++ b/src/forms/reset-password-popup/forgot-password/ForgotPasswordPage.jsx
@@ -0,0 +1,95 @@
+import React, { useState } from 'react';
+
+import { getConfig } from '@edx/frontend-platform';
+import { useIntl } from '@edx/frontend-platform/i18n';
+import {
+ Button, Container, Form,
+} from '@openedx/paragon';
+
+import ForgotPasswordEmailSentConfirmation from './ForgotPasswordEmailSentConfirmation';
+import { InlineLink } from '../../../common-ui';
+import EmailField from '../../fields/email-field';
+import messages from '../messages';
+import ResetPasswordHeader from '../ResetPasswordHeader';
+import '../index.scss';
+
+/**
+ * ForgotPasswordForm component for handling user password reset.
+ * This component provides a form for users to reset their password by entering their email.
+ */
+const ForgotPasswordPage = () => {
+ const { formatMessage } = useIntl();
+
+ const [formFields, setFormFields] = useState({ email: '' });
+ const [isSuccess, setIsSuccess] = useState(false);
+
+ const handleOnChange = (event) => {
+ const { name } = event.target;
+ const value = event.target.type === 'checkbox' ? event.target.checked : event.target.value;
+ setFormFields(prevState => ({ ...prevState, [name]: value }));
+ };
+
+ const handleSubmit = (e) => {
+ e.preventDefault();
+ setIsSuccess(true);
+ };
+
+ return (
+
+
+ {!isSuccess && (
+
+ )}
+ {isSuccess && (
+
+ )}
+
+
+ );
+};
+
+export default ForgotPasswordPage;
diff --git a/src/forms/reset-password-popup/index.scss b/src/forms/reset-password-popup/index.scss
new file mode 100644
index 00000000..0d011082
--- /dev/null
+++ b/src/forms/reset-password-popup/index.scss
@@ -0,0 +1,13 @@
+@import "~@edx/brand/paragon/variables";
+
+.separator {
+ border: 0;
+ border-top: 0.075rem solid $light-500
+}
+.back-to-login__button {
+ border-color: $primary-500;
+ border: 2px solid;
+}
+#set-reset-password-form .pgn__form-control-floating-label-text:after {
+ content: none;
+}
diff --git a/src/forms/reset-password-popup/messages.js b/src/forms/reset-password-popup/messages.js
new file mode 100644
index 00000000..82f4d709
--- /dev/null
+++ b/src/forms/reset-password-popup/messages.js
@@ -0,0 +1,94 @@
+import { defineMessages } from '@edx/frontend-platform/i18n';
+
+const messages = defineMessages({
+ resetPasswordFormHeading: {
+ id: 'reset.password.form.heading',
+ defaultMessage: 'Reset Password',
+ description: 'Reset password form main heading',
+ },
+ resetPasswordFormSubmitButton: {
+ id: 'reset.password.form.submit.button',
+ defaultMessage: 'Submit',
+ description: 'Text for submit button on reset password form',
+ },
+ resetPasswordFormNeedHelpText: {
+ id: 'reset.password.form.need.help.text',
+ defaultMessage: 'Need help signing in?',
+ description: 'reset Password help text',
+ },
+ resetPasswordFormHelpCenterLink: {
+ id: 'reset.password.form.help.center.link',
+ defaultMessage: 'Help center',
+ description: 'Text for help center link',
+ },
+ resetPasswordFormAdditionalHelpText: {
+ id: 'reset.password.form.additional.help.text',
+ defaultMessage: 'For additional help, contact edX support at',
+ description: 'Label for link that leads learners to the email page',
+ },
+ resetPasswordBackToLoginButton: {
+ id: 'reset.password.back.to.login.button',
+ defaultMessage: 'Back to login',
+ description: 'Text for back to login button on reset password form',
+ },
+ newPasswordLabel: {
+ id: 'new.password.label',
+ defaultMessage: 'New password',
+ description: 'New password field label for the reset password page.',
+ },
+ confirmPasswordLabel: {
+ id: 'confirm.password.label',
+ defaultMessage: 'Confirm password',
+ description: 'Confirm password field label for the reset password page.',
+ },
+ resetPasswordButton: {
+ id: 'reset.password.button',
+ defaultMessage: 'Reset password',
+ description: 'Button text for reset password popup.',
+ },
+ enterConfirmPasswordMessage: {
+ id: 'enter.confirm.password.message',
+ defaultMessage: 'Enter and confirm the new password',
+ description: 'Message for entering and confirming the new password',
+ },
+ // email sent messages
+ emailSentMessage: {
+ id: 'email.sent.message',
+ defaultMessage: 'Email has been sent',
+ description: 'Notification message indicating that an email has been sent',
+ },
+ // validation errors
+ passwordRequiredMessage: {
+ id: 'password.required.message',
+ defaultMessage: 'Password is a required field',
+ description: 'Error message for empty password',
+ },
+ passwordValidationMessage: {
+ id: 'password.validation.message',
+ defaultMessage: 'Password criteria has not been met',
+ description: 'Error message for invalid password',
+ },
+ passwordDoNotMatch: {
+ id: 'passwords.do.not.match',
+ defaultMessage: 'Passwords do not match',
+ description: 'Password format error.',
+ },
+ confirmYourPassword: {
+ id: 'confirm.your.password',
+ defaultMessage: 'Confirm your password',
+ description: 'Field validation message when confirm password is empty',
+ },
+ // alert banner strings
+ resetPasswordFailureHeading: {
+ id: 'reset.password.failure.heading',
+ defaultMessage: 'We couldn\'t reset your password.',
+ description: 'Heading for reset password request failure',
+ },
+ forgotPasswordFormEmailFieldLabel: {
+ id: 'forgot.Password.form.email.label',
+ defaultMessage: 'Email',
+ description: 'Label for email input field',
+ },
+});
+
+export default messages;
diff --git a/src/forms/reset-password-popup/reset-password/ResetPasswordFailure.jsx b/src/forms/reset-password-popup/reset-password/ResetPasswordFailure.jsx
new file mode 100644
index 00000000..429d09f3
--- /dev/null
+++ b/src/forms/reset-password-popup/reset-password/ResetPasswordFailure.jsx
@@ -0,0 +1,29 @@
+import React from 'react';
+
+import { useIntl } from '@edx/frontend-platform/i18n';
+import { Alert } from '@openedx/paragon';
+
+import messages from '../messages';
+
+/**
+ * Component to display reset password failure message.
+ * @returns {JSX.Element | null} The rendered failure message component or null if no error.
+ */
+const ResetPasswordFailure = () => {
+ const { formatMessage } = useIntl();
+
+ const errorMessage = ' ';
+ const heading = formatMessage(messages.resetPasswordFailureHeading);
+
+ if (errorMessage) {
+ return (
+
+ {heading}
+
+ );
+ }
+
+ return null;
+};
+
+export default ResetPasswordFailure;
diff --git a/src/forms/reset-password-popup/reset-password/ResetPasswordPage.jsx b/src/forms/reset-password-popup/reset-password/ResetPasswordPage.jsx
new file mode 100644
index 00000000..a29bf54c
--- /dev/null
+++ b/src/forms/reset-password-popup/reset-password/ResetPasswordPage.jsx
@@ -0,0 +1,115 @@
+import React, { useState } from 'react';
+
+import { useIntl } from '@edx/frontend-platform/i18n';
+import {
+ Button, Container, Form,
+} from '@openedx/paragon';
+import PropTypes from 'prop-types';
+
+import ResetPasswordFailure from './ResetPasswordFailure';
+import PasswordField from '../../fields/password-field';
+import messages from '../messages';
+import ResetPasswordHeader from '../ResetPasswordHeader';
+
+export const LETTER_REGEX = /[a-zA-Z]/;
+export const NUMBER_REGEX = /\d/;
+
+/**
+ * ResetPasswordForm component for completing user password reset.
+ * This component provides a form for users to reset their password.
+ * @param {string} newPassword - The new password entered by the user.
+ * @param {string} confirmNewPassword - The confirmation of the new password entered by the user.
+ * @returns {string} A message indicating the success or failure of the password reset process.
+ */
+const ResetPasswordPage = (props) => {
+ const { formatMessage } = useIntl();
+ const [newPassword, setNewPassword] = useState('');
+ const [confirmPassword, setConfirmPassword] = useState('');
+ const [formErrors, setFormErrors] = useState({});
+
+ const validateInput = (name, value) => {
+ switch (name) {
+ case 'newPassword':
+ if (!value) {
+ formErrors.newPassword = formatMessage(messages.passwordRequiredMessage);
+ } else if (!LETTER_REGEX.test(value) || !NUMBER_REGEX.test(value) || value.length < 8) {
+ formErrors.newPassword = formatMessage(messages.passwordValidationMessage);
+ } else {
+ // will add backend validation message
+ }
+ break;
+ case 'confirmPassword':
+ if (!value) {
+ formErrors.confirmPassword = formatMessage(messages.confirmYourPassword);
+ } else if (value !== newPassword) {
+ formErrors.confirmPassword = formatMessage(messages.passwordDoNotMatch);
+ } else {
+ formErrors.confirmPassword = '';
+ }
+ break;
+ default:
+ break;
+ }
+ setFormErrors({ ...formErrors });
+ return !Object.values(formErrors).some(x => (x !== ''));
+ };
+ const handleOnBlur = (event) => {
+ const { name, value } = event.target;
+ validateInput(name, value);
+ };
+ const handleOnFocus = (e) => {
+ setFormErrors({ ...formErrors, [e.target.name]: '' });
+ };
+
+ return (
+
+
+
+
+ {formatMessage(messages.enterConfirmPasswordMessage)}
+
+
+ );
+};
+
+ResetPasswordPage.defaultProps = {
+ errorMsg: null,
+};
+
+ResetPasswordPage.propTypes = {
+ errorMsg: PropTypes.string,
+};
+
+export default ResetPasswordPage;
diff --git a/src/index.jsx b/src/index.jsx
deleted file mode 100644
index e69de29b..00000000
diff --git a/src/index.scss b/src/index.scss
index 1d5f370e..ed9e89d4 100644
--- a/src/index.scss
+++ b/src/index.scss
@@ -5,7 +5,7 @@
padding: 1.75rem 1rem;
}
-.authn__popup-container > .pgn__form-control-floating-label-text:after {
+.authn__popup-container .pgn__form-control-floating-label-text:after {
content:"*";
color: $danger-500;
}