diff --git a/frontend/src/common/i18n/en/auth.json b/frontend/src/common/i18n/en/auth.json new file mode 100644 index 00000000..20e163e2 --- /dev/null +++ b/frontend/src/common/i18n/en/auth.json @@ -0,0 +1,35 @@ +{ + "email-verification": { + "title": "Email Verification", + "message": "Please enter the verification code sent to your email address.", + "success": "Your email has been successfully verified.", + "code-resent": "A new verification code has been sent to your email." + }, + "validation": { + "required": "This field is required.", + "numeric": "This field must contain only numbers.", + "exact-length": "This field must be exactly {{length}} characters." + }, + "error": { + "no-email": "Email address not found. Please try again." + }, + "label": { + "verification-code": "Verification code", + "email": "Email" + }, + "confirm": "Confirm", + "resend-code": "Request new code", + "verification": { + "loading": "Verifying your code...", + "enter-code": "Please enter the verification code we sent to" + }, + "confirm-account": { + "title": "Confirm Account", + "message": "Please enter the verification code we sent to", + "submit": "Confirm Account" + }, + "request-new-code": "Request new code", + "back-to-login": "Back to Log in", + "signin": "Log in", + "logo-alt": "MEDReport AI Logo" +} diff --git a/frontend/src/common/i18n/en/common.json b/frontend/src/common/i18n/en/common.json new file mode 100644 index 00000000..755daef9 --- /dev/null +++ b/frontend/src/common/i18n/en/common.json @@ -0,0 +1,13 @@ +{ + "logo-alt": "MEDReport AI Logo", + "app": { + "name": "MEDReport AI" + }, + "confirm": "Confirm", + "cancel": "Cancel", + "loading": "Loading...", + "error": "Error", + "success": "Success", + "back": "Back", + "next": "Next" +} diff --git a/frontend/src/common/utils/i18n/resources/en/auth.json b/frontend/src/common/utils/i18n/resources/en/auth.json index 27fcb35e..b2f41e63 100644 --- a/frontend/src/common/utils/i18n/resources/en/auth.json +++ b/frontend/src/common/utils/i18n/resources/en/auth.json @@ -41,7 +41,7 @@ "or-signin-with": "Or sign in with", "submit": "Submit", "confirm": "Confirm", - "resend-code": "Resend Code", + "resend-code": "Request new code", "forgot-password": "Forgot Password?", "password-recovery": { "title": "Password Recovery", @@ -62,6 +62,16 @@ "success": "Email verified successfully!", "code-resent": "A new verification code has been sent to your email." }, + "confirm-account": { + "title": "Confirm Account", + "message": "Please enter the verification code we sent to", + "submit": "Confirm Account" + }, + "back-to-login": "Back to Log in", + "registration": { + "success": "Registration successful!", + "verify-email": "Please verify your email to activate your account." + }, "oauth": { "processing": "Processing your login...", "redirecting": "Redirecting...", @@ -80,5 +90,6 @@ "uppercase": "Must contain at least one uppercase letter", "number": "Must contain at least one number", "special-char": "Must contain at least one special character" - } + }, + "please-fill-details": "Please fill in your personal details" } diff --git a/frontend/src/common/utils/i18n/resources/es/auth.json b/frontend/src/common/utils/i18n/resources/es/auth.json index 350ad8b8..8695f245 100644 --- a/frontend/src/common/utils/i18n/resources/es/auth.json +++ b/frontend/src/common/utils/i18n/resources/es/auth.json @@ -16,5 +16,10 @@ }, "signin": "Iniciar sesión", "signin.title": "Iniciar sesión", - "signin.subtitle": "Iniciar sesión para acceder a MEDReport AI" + "signin.subtitle": "Iniciar sesión para acceder a MEDReport AI", + "registration": { + "success": "¡Registro exitoso!", + "verify-email": "Por favor verifica tu correo electrónico para activar tu cuenta." + }, + "please-fill-details": "Por favor complete sus datos personales" } diff --git a/frontend/src/common/utils/i18n/resources/fr/auth.json b/frontend/src/common/utils/i18n/resources/fr/auth.json index 11a4abfe..99fa750d 100644 --- a/frontend/src/common/utils/i18n/resources/fr/auth.json +++ b/frontend/src/common/utils/i18n/resources/fr/auth.json @@ -16,5 +16,10 @@ }, "signin": "Se connecter", "signin.title": "Se connecter", - "signin.subtitle": "Connectez-vous pour accéder à MEDReport AI" + "signin.subtitle": "Connectez-vous pour accéder à MEDReport AI", + "registration": { + "success": "Inscription réussie !", + "verify-email": "Veuillez vérifier votre e-mail pour activer votre compte." + }, + "please-fill-details": "Veuillez remplir vos coordonnées personnelles" } diff --git a/frontend/src/pages/Auth/SignUp/components/SignUpForm.scss b/frontend/src/pages/Auth/SignUp/components/SignUpForm.scss index c6d0ffdc..9c5e723a 100644 --- a/frontend/src/pages/Auth/SignUp/components/SignUpForm.scss +++ b/frontend/src/pages/Auth/SignUp/components/SignUpForm.scss @@ -82,9 +82,10 @@ &-icon { margin-right: 0.375rem; font-size: 1.1rem; + color: var(--ion-color-medium); } - &-valid { + &.valid { color: var(--ion-color-success); } @@ -93,4 +94,37 @@ } } } + + &__success { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + text-align: center; + padding: 2rem 1.5rem; + background-color: #f8fff8; + border: 1px solid var(--ion-color-success); + border-radius: 8px; + margin: 1rem 0; + + &-icon { + font-size: 4rem; + margin-bottom: 1rem; + } + + h3 { + font-size: 1.4rem; + font-weight: 600; + margin: 0 0 0.75rem; + color: var(--ion-color-success); + } + + p { + font-size: 1rem; + margin: 0; + color: #666; + line-height: 1.5; + max-width: 320px; + } + } } diff --git a/frontend/src/pages/Auth/SignUp/components/SignUpForm.tsx b/frontend/src/pages/Auth/SignUp/components/SignUpForm.tsx index 50291afa..4620502c 100644 --- a/frontend/src/pages/Auth/SignUp/components/SignUpForm.tsx +++ b/frontend/src/pages/Auth/SignUp/components/SignUpForm.tsx @@ -13,7 +13,7 @@ import classNames from 'classnames'; import { Form, Formik, useFormikContext } from 'formik'; import { object, string, ref } from 'yup'; import { useTranslation } from 'react-i18next'; -import { checkmarkCircle, ellipseOutline } from 'ionicons/icons'; +import { checkmarkCircle, checkmarkCircleOutline } from 'ionicons/icons'; import './SignUpForm.scss'; import { BaseComponentProps } from 'common/components/types'; @@ -41,6 +41,27 @@ interface SignUpFormValues { confirmPassword: string; } +/** + * Registration success message component + */ +const RegistrationSuccess = ({ email }: { email: string }) => { + const { t } = useTranslation(); + + return ( +
+ +

{t('registration.success', { ns: 'auth' })}

+

+ {t('registration.verify-email', { + ns: 'auth', + email, + defaultValue: 'Please verify your email to activate your account.', + })} +

+
+ ); +}; + /** * Password guidelines component */ @@ -62,7 +83,7 @@ const PasswordGuidelines = () => { }`} > {text} @@ -89,6 +110,8 @@ const SignUpForm = ({ className, testid = 'form-signup' }: SignUpFormProps): JSX const focusInput = useRef(null); const [error, setError] = useState(null); const [isLoading, setIsLoading] = useState(false); + const [registrationSuccess, setRegistrationSuccess] = useState(false); + const [registeredEmail, setRegisteredEmail] = useState(''); const { setIsActive: setShowProgress } = useProgress(); const router = useIonRouter(); const { signUp, isLoading: isSignUpLoading } = useSignUp(); @@ -124,6 +147,13 @@ const SignUpForm = ({ className, testid = 'form-signup' }: SignUpFormProps): JSX focusInput.current?.setFocus(); }); + // Redirect to verification page after showing success message + const handleRedirectAfterSuccess = () => { + setTimeout(() => { + router.push('/auth/verify', 'forward', 'replace'); + }, 3000); // Show success message for 3 seconds before redirecting + }; + return (
- - initialValues={{ - email: '', - firstName: '', - lastName: '', - password: '', - confirmPassword: '', - }} - onSubmit={async (values, { setSubmitting }) => { - try { - setError(null); - setIsLoading(true); - setShowProgress(true); - await signUp(values.email, values.password, values.firstName, values.lastName); + {registrationSuccess ? ( + + ) : ( + + initialValues={{ + email: '', + firstName: '', + lastName: '', + password: '', + confirmPassword: '', + }} + onSubmit={async (values, { setSubmitting }) => { + try { + setError(null); + setIsLoading(true); + setShowProgress(true); + await signUp(values.email, values.password, values.firstName, values.lastName); - // Store the email in sessionStorage for the verification page - sessionStorage.setItem('verification_email', values.email); + // Store the email in sessionStorage for the verification page + sessionStorage.setItem('verification_email', values.email); - // Show success briefly before redirecting - setIsLoading(false); + // Show success message + setIsLoading(false); + setRegisteredEmail(values.email); + setRegistrationSuccess(true); - // Navigate to verification page - router.push('/auth/verify', 'forward', 'replace'); - } catch (err) { - setError(formatAuthError(err)); - } finally { - setShowProgress(false); - setSubmitting(false); - setIsLoading(false); - } - }} - validationSchema={validationSchema} - > - {({ dirty, isSubmitting, isValid }) => ( -
-
-

{t('signup', { ns: 'auth' })}

-

- {t('please-fill-details', { - ns: 'auth', - defaultValue: 'Please fill in your personal details', - })} -

+ // Redirect after showing success message + handleRedirectAfterSuccess(); + } catch (err) { + setError(formatAuthError(err)); + } finally { + setShowProgress(false); + setSubmitting(false); + setIsLoading(false); + } + }} + validationSchema={validationSchema} + > + {({ dirty, isSubmitting, isValid }) => ( + +
+

{t('signup', { ns: 'auth' })}

+

+ {t('please-fill-details', { + ns: 'auth', + defaultValue: 'Please fill in your personal details', + })} +

- + - + - + - - - + + + - - - + + + - + - - {t('signup.button', { ns: 'auth' })} - + + {t('signup.button', { ns: 'auth' })} + - - - - {t('already-have-account', { ns: 'auth' })}{' '} - {t('signin', { ns: 'auth' })} - - - -
- - )} - + + + + {t('already-have-account', { ns: 'auth' })}{' '} + {t('signin', { ns: 'auth' })} + + + +
+ + )} + + )}
); }; diff --git a/frontend/src/pages/Auth/Verify/VerificationPage.scss b/frontend/src/pages/Auth/Verify/VerificationPage.scss index b304a641..df188d80 100644 --- a/frontend/src/pages/Auth/Verify/VerificationPage.scss +++ b/frontend/src/pages/Auth/Verify/VerificationPage.scss @@ -1,13 +1,53 @@ .ls-verification-page { - .ls-verification-page__container { - max-width: 32rem; + --ion-background-color: transparent; + + &__background { + width: 100%; height: 100%; display: flex; flex-direction: column; + align-items: center; + justify-content: flex-start; + padding: 2rem 1.5rem; + background-color: #1c1e2e; + background-image: url('../../../assets/Splash.png'); + background-size: cover; + background-position: center; + position: relative; + } + + &__logo-container { + display: flex; + align-items: center; justify-content: center; + margin-bottom: 2.5rem; + margin-top: 2rem; + } + + &__logo { + width: 3.5rem; + height: auto; + margin-right: 0.75rem; + } + + &__logo-text { + color: white; + font-size: 1.75rem; + font-weight: 600; + } + + &__card { + width: 100%; + max-width: 30rem; + background-color: white; + border-radius: 1.5rem; + padding: 2.5rem 2rem; + box-shadow: 0 0.5rem 1.5rem rgb(0 0 0 / 15%); + z-index: 1; } - .ls-verification-page__form { + &__form { width: 100%; + margin: 0; } } diff --git a/frontend/src/pages/Auth/Verify/VerificationPage.tsx b/frontend/src/pages/Auth/Verify/VerificationPage.tsx index 9aa331a1..c54d847a 100644 --- a/frontend/src/pages/Auth/Verify/VerificationPage.tsx +++ b/frontend/src/pages/Auth/Verify/VerificationPage.tsx @@ -1,13 +1,12 @@ -import { IonContent, IonPage } from '@ionic/react'; +import { IonContent, IonPage, IonImg } from '@ionic/react'; import { useTranslation } from 'react-i18next'; import { useEffect, useState } from 'react'; import './VerificationPage.scss'; import { PropsWithTestId } from 'common/components/types'; import ProgressProvider from 'common/providers/ProgressProvider'; -import Header from 'common/components/Header/Header'; import VerificationForm from './components/VerificationForm'; -import Container from 'common/components/Content/Container'; +import logo from '../../../assets/logo_ls.png'; /** * Properties for the `VerificationPage` component. @@ -34,12 +33,23 @@ const VerificationPage = ({ testid = 'page-verification' }: VerificationPageProp return ( -
+ +
+
+ + + {t('app.name', { ns: 'common' })} + +
- - - - +
+ +
+
diff --git a/frontend/src/pages/Auth/Verify/components/VerificationForm.scss b/frontend/src/pages/Auth/Verify/components/VerificationForm.scss index 6f03a82d..575cef2e 100644 --- a/frontend/src/pages/Auth/Verify/components/VerificationForm.scss +++ b/frontend/src/pages/Auth/Verify/components/VerificationForm.scss @@ -1,34 +1,53 @@ .ls-verification-form { - padding: 1rem; + width: 100%; - .ls-verification-form__message { - margin: 1rem 0; - display: flex; - flex-direction: column; - gap: 0.5rem; + .ls-verification-form__title { + font-size: 2rem; + font-weight: 600; + color: #333; + margin-bottom: 1.25rem; + margin-top: 0; + text-align: center; } - .ls-verification-form__email { - display: block; - margin-top: 0.5rem; + .ls-verification-form__message { + margin: 1rem 0 1.5rem; + text-align: center; + color: #666; + font-size: 1rem; + line-height: 1.5; } .ls-verification-form__input { - margin-bottom: 1rem; + margin-bottom: 1.5rem; } .ls-verification-form__button { margin-top: 1rem; + margin-bottom: 0.75rem; + height: 3rem; + font-weight: 500; + + --border-radius: 0.75rem; + } + + .ls-verification-form__resend-button { + margin-bottom: 1.5rem; + height: 3rem; + font-weight: 500; + + --border-radius: 0.75rem; } - .ls-verification-form__resend { + .ls-verification-form__back-link { display: flex; justify-content: center; margin-top: 1rem; + font-size: 0.9rem; } .ls-verification-form__success { - background-color: rgba(45, 211, 111, 0.1); + background-color: rgb(45 211 111 / 10%); border-radius: 8px; padding: 12px; margin-bottom: 1rem; diff --git a/frontend/src/pages/Auth/Verify/components/VerificationForm.tsx b/frontend/src/pages/Auth/Verify/components/VerificationForm.tsx index 8620d344..231d788e 100644 --- a/frontend/src/pages/Auth/Verify/components/VerificationForm.tsx +++ b/frontend/src/pages/Auth/Verify/components/VerificationForm.tsx @@ -11,7 +11,6 @@ import { AuthError } from 'common/models/auth'; import { useAuth } from 'common/hooks/useAuth'; import { useProgress } from 'common/hooks/useProgress'; import Input from 'common/components/Input/Input'; -import HeaderRow from 'common/components/Text/HeaderRow'; import { formatAuthError } from 'common/utils/auth-errors'; import AuthErrorDisplay from 'common/components/Auth/AuthErrorDisplay'; import AuthLoadingIndicator from 'common/components/Auth/AuthLoadingIndicator'; @@ -152,17 +151,14 @@ const VerificationForm = ({ > {({ dirty, isSubmitting }) => (
- -
{t('email-verification.title', { ns: 'auth' })}
-
+

+ {t('confirm-account.title', { ns: 'auth' })} +

- {t('email-verification.message', { ns: 'auth' })} - {email && ( - - {email} - - )} + + {t('confirm-account.message', { ns: 'auth' })} {email && {email}} +
- {t('confirm', { ns: 'auth' })} + {t('confirm-account.submit', { ns: 'auth' })} -
+ + {t('resend-code', { ns: 'auth' })} + + +
- {t('resend-code', { ns: 'auth' })} + ← {t('back-to-login', { ns: 'auth' })}