Compare commits
13 Commits
open-relea
...
open-relea
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1e0be4e581 | ||
|
|
8b2160388f | ||
|
|
ab357704f8 | ||
|
|
2eab85e960 | ||
|
|
9f503eb5ef | ||
|
|
c2a55e125c | ||
|
|
abea379eb0 | ||
|
|
48122ff99d | ||
|
|
3153cff4ff | ||
|
|
919d98df79 | ||
|
|
c7adcecb8a | ||
|
|
db4eeac266 | ||
|
|
60e2119116 |
10
package-lock.json
generated
10
package-lock.json
generated
@@ -10,7 +10,7 @@
|
||||
"license": "AGPL-3.0",
|
||||
"dependencies": {
|
||||
"@edx/brand": "npm:@edx/brand-openedx@1.2.0",
|
||||
"@edx/frontend-platform": "^5.0.0",
|
||||
"@edx/frontend-platform": "^5.5.4",
|
||||
"@edx/paragon": "20.46.2",
|
||||
"@fortawesome/fontawesome-svg-core": "6.4.2",
|
||||
"@fortawesome/free-brands-svg-icons": "6.4.2",
|
||||
@@ -3350,9 +3350,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@edx/frontend-platform": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@edx/frontend-platform/-/frontend-platform-5.0.0.tgz",
|
||||
"integrity": "sha512-DD9/B4rnC3BKPiWlbEFF1JIYFbWC6vUBKTyN8sf4khi4DNhhWhsobk+iNeCWNzF9UgCPRbniIqesdV1F9NXNZw==",
|
||||
"version": "5.5.4",
|
||||
"resolved": "https://registry.npmjs.org/@edx/frontend-platform/-/frontend-platform-5.5.4.tgz",
|
||||
"integrity": "sha512-Yum+oST7XfDwDnDhBnzeR/mjp6O+G0g+5AZtIJ1BdTKQH1z9FObfim/pfoiI9STiYlguVpeWMkzWuca/QMLO/Q==",
|
||||
"dependencies": {
|
||||
"@cospired/i18n-iso-languages": "4.1.0",
|
||||
"@formatjs/intl-pluralrules": "4.3.3",
|
||||
@@ -3380,7 +3380,7 @@
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@edx/frontend-build": ">= 8.1.0 || ^12.9.0-alpha.1",
|
||||
"@edx/paragon": ">= 10.0.0 < 21.0.0",
|
||||
"@edx/paragon": ">= 10.0.0 < 22.0.0",
|
||||
"prop-types": "^15.7.2",
|
||||
"react": "^16.9.0 || ^17.0.0",
|
||||
"react-dom": "^16.9.0 || ^17.0.0",
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@edx/brand": "npm:@edx/brand-openedx@1.2.0",
|
||||
"@edx/frontend-platform": "^5.0.0",
|
||||
"@edx/frontend-platform": "^5.5.4",
|
||||
"@edx/paragon": "20.46.2",
|
||||
"@fortawesome/fontawesome-svg-core": "6.4.2",
|
||||
"@fortawesome/free-brands-svg-icons": "6.4.2",
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
<!doctype html>
|
||||
<html lang="en-us">
|
||||
<head>
|
||||
<title>Authn | <%= process.env.SITE_NAME %></title>
|
||||
<title><%= (process.env.SITE_NAME && process.env.SITE_NAME != 'null') ? 'Authentication | ' + process.env.SITE_NAME : 'Authentication' %></title>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link rel="shortcut icon" href="<%=htmlWebpackPlugin.options.FAVICON_URL%>" type="image/x-icon" />
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/iframe-resizer/4.3.6/iframeResizer.contentWindow.min.js"
|
||||
integrity="sha512-R7Piufj0/o6jG9ZKrAvS2dblFr2kkuG4XVQwStX+/4P+KwOLUXn2DXy0l1AJDxxqGhkM/FJllZHG2PKOAheYzg=="
|
||||
crossorigin="anonymous"
|
||||
|
||||
@@ -23,13 +23,15 @@ const MediumLayout = () => {
|
||||
<div>
|
||||
<h1
|
||||
className={classNames(
|
||||
'display-1 text-white mt-5 mb-5 mr-2',
|
||||
'display-1 text-white mt-5 mb-5 mr-2 main-heading',
|
||||
{ 'ml-4.5': getConfig().SITE_NAME !== 'edX' },
|
||||
)}
|
||||
>
|
||||
<span className="mr-2">{formatMessage(messages['start.learning'])}</span>
|
||||
<span className="text-accent-a d-inline-block">
|
||||
{formatMessage(messages['with.site.name'], { siteName: getConfig().SITE_NAME })}
|
||||
<span>
|
||||
{formatMessage(messages['start.learning'])}{' '}
|
||||
<span className="text-accent-a d-inline-block">
|
||||
{formatMessage(messages['with.site.name'], { siteName: getConfig().SITE_NAME })}
|
||||
</span>
|
||||
</span>
|
||||
</h1>
|
||||
</div>
|
||||
|
||||
@@ -17,17 +17,18 @@ const SmallLayout = () => {
|
||||
<Hyperlink destination={getConfig().MARKETING_SITE_BASE_URL}>
|
||||
<Image className="logo-small" alt={getConfig().SITE_NAME} src={getConfig().LOGO_WHITE_URL} />
|
||||
</Hyperlink>
|
||||
<div className="d-flex align-items-center mb-3 mt-3 mr-3">
|
||||
<div className="d-flex align-items-center m-3.5">
|
||||
<div className={classNames({ 'small-yellow-line mr-n2.5': getConfig().SITE_NAME === 'edX' })} />
|
||||
<h1
|
||||
className={classNames(
|
||||
'text-white mt-3.5 mb-3.5',
|
||||
{ 'ml-4.5': getConfig().SITE_NAME !== 'edX' },
|
||||
)}
|
||||
>
|
||||
<span className="mr-1">{formatMessage(messages['start.learning'])}</span>
|
||||
<span className="text-accent-a d-inline-block">
|
||||
{formatMessage(messages['with.site.name'], { siteName: getConfig().SITE_NAME })}
|
||||
<span>
|
||||
{formatMessage(messages['start.learning'])}{' '}
|
||||
<span className="text-accent-a d-inline-block">
|
||||
{formatMessage(messages['with.site.name'], { siteName: getConfig().SITE_NAME })}
|
||||
</span>
|
||||
</span>
|
||||
</h1>
|
||||
</div>
|
||||
|
||||
@@ -16,7 +16,7 @@ const SmallLayout = ({ username }) => {
|
||||
<Hyperlink destination={getConfig().MARKETING_SITE_BASE_URL}>
|
||||
<Image className="logo-small" alt={getConfig().SITE_NAME} src={getConfig().LOGO_URL} />
|
||||
</Hyperlink>
|
||||
<div className="d-flex align-items-center mb-3 mt-3 mr-3">
|
||||
<div className="d-flex align-items-center m-3.5">
|
||||
<div className="small-yellow-line mt-4.5" />
|
||||
<div>
|
||||
<h1 className="h5 data-hj-suppress">
|
||||
|
||||
@@ -71,7 +71,7 @@
|
||||
"non.compliant.password.message": "كلمة مرورك الحالية لا تستسجيب لمتطلبات الأمان الجديدة. لقد أرسلنا للتو رسالة لإعادة ضبط كلمة المرور إلى عنوان البريد الإلكتروني المرتبط بهذا الحساب. شكرًا لك على مساعدتنا في الحفاظ على سلامة بياناتك.",
|
||||
"account.locked.out.message.1": "لحماية حسابك، تم إقفاله مؤقتًا. حاول مرة أخرى بعد 30 دقيقة.",
|
||||
"enterprise.login.btn.text": "بيانات الشركة أو المدرسة",
|
||||
"username.or.email.format.validation.less.chars.message": "يجب أن يحتوي اسم المستخدم أو البريد الإلكتروني على 3 أحرف على الأقل.",
|
||||
"username.or.email.format.validation.less.chars.message": "يجب أن يحتوي اسم المستخدم أو البريد الإلكتروني على 2 أحرف على الأقل.",
|
||||
"email.validation.message": "أدخل اسم المستخدم أو البريد الإلكتروني الخاص بك",
|
||||
"password.validation.message": "لم يتم استيفاء معايير كلمة المرور",
|
||||
"account.activation.success.message.title": "نجح الأمر! لقد قمت بتفعيل حسابك.",
|
||||
@@ -177,4 +177,4 @@
|
||||
"reset.password.success.heading": "تمت إعادة ضبط كلمة المرور.",
|
||||
"reset.password.success": "تمت إعادة ضبط كلمة مرورك. سجل الدخول إلى حسابك.",
|
||||
"rate.limit.error": "حدث خطأ بسبب كثرة الطلبات. رجاءً حاول مرة أخرى بعد مضي بعض الوقت."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,7 +71,7 @@
|
||||
"non.compliant.password.message": "Your current password does not meet the new security requirements. We just sent a password-reset message to the email address associated with this account. Thank you for helping us keep your data safe.",
|
||||
"account.locked.out.message.1": "To protect your account, it's been temporarily locked. Try again in 30 minutes.",
|
||||
"enterprise.login.btn.text": "Company or school credentials",
|
||||
"username.or.email.format.validation.less.chars.message": "Username or email must have at least 3 characters.",
|
||||
"username.or.email.format.validation.less.chars.message": "Username or email must have at least 2 characters.",
|
||||
"email.validation.message": "Enter your username or email",
|
||||
"password.validation.message": "Password criteria has not been met",
|
||||
"account.activation.success.message.title": "Success! You have activated your account.",
|
||||
@@ -177,4 +177,4 @@
|
||||
"reset.password.success.heading": "Password reset complete.",
|
||||
"reset.password.success": "Your password has been reset. Sign in to your account.",
|
||||
"rate.limit.error": "An error has occurred because of too many requests. Please try again after some time."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,7 +71,7 @@
|
||||
"non.compliant.password.message": "Ihr aktuelles Passwort entspricht nicht den neuen Sicherheitsanforderungen. Wir haben gerade eine Nachricht zum Zurücksetzen des Passworts an die mit diesem Konto verknüpfte E-Mail-Adresse gesendet. Vielen Dank, dass Sie uns helfen, Ihre Daten zu schützen.",
|
||||
"account.locked.out.message.1": "Um Ihr Konto zu schützen, wurde es vorübergehend gesperrt. Versuchen Sie es in 30 Minuten erneut.",
|
||||
"enterprise.login.btn.text": "Arbeits- oder Schulzeugnisse",
|
||||
"username.or.email.format.validation.less.chars.message": "Benutzername oder E-Mail müssen mindestens 3 Zeichen lang sein.",
|
||||
"username.or.email.format.validation.less.chars.message": "Benutzername oder E-Mail müssen mindestens 2 Zeichen lang sein.",
|
||||
"email.validation.message": "Geben Sie Ihren Benutzernamen oder Ihre E-Mail-Adresse ein",
|
||||
"password.validation.message": "Die Passwortkriterien wurden nicht erfüllt",
|
||||
"account.activation.success.message.title": "Super! Sie haben Ihr Konto aktiviert.",
|
||||
@@ -177,4 +177,4 @@
|
||||
"reset.password.success.heading": "Zurücksetzen des Passworts abgeschlossen.",
|
||||
"reset.password.success": "Ihr Passwort wurde zurückgesetzt. Melden Sie sich bei Ihrem Konto an.",
|
||||
"rate.limit.error": "Aufgrund zu vieler Anfragen ist ein Fehler aufgetreten. Bitte versuchen Sie es nach einiger Zeit erneut."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,7 +71,7 @@
|
||||
"non.compliant.password.message": "Tu contraseña actual no cumple con los nuevos requisitos de seguridad. Acabamos de enviar un mensaje de restablecimiento de contraseña a la dirección de correo electrónico asociada a esta cuenta. Gracias por ayudarnos a mantener tus datos seguros.",
|
||||
"account.locked.out.message.1": "Para proteger tu cuenta, se ha bloqueado temporalmente. Inténtalo de nuevo en 30 minutos.",
|
||||
"enterprise.login.btn.text": "Credenciales de la empresa o de la institución ",
|
||||
"username.or.email.format.validation.less.chars.message": "El nombre de usuario o el correo electrónico deben tener al menos 3 caracteres.",
|
||||
"username.or.email.format.validation.less.chars.message": "El nombre de usuario o el correo electrónico deben tener al menos 2 caracteres.",
|
||||
"email.validation.message": "Introduce tu nombre de usuario o correo electrónico",
|
||||
"password.validation.message": "No se han cumplido los criterios de la contraseña",
|
||||
"account.activation.success.message.title": "Ha sido un éxito. Has activado tu cuenta.",
|
||||
@@ -177,4 +177,4 @@
|
||||
"reset.password.success.heading": "Restablecimiento de la contraseña completado.",
|
||||
"reset.password.success": "Tu contraseña ha sido restablecida. Acceda a tu cuenta.",
|
||||
"rate.limit.error": "Se ha producido un error debido a demasiadas solicitudes. Por favor, inténtalo de nuevo después de algún tiempo."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,7 +71,7 @@
|
||||
"non.compliant.password.message": "گذرواژه فعلی شما الزامات امنیتی جدید را برآورده نمیکند. ما فقط یک پیام بازتنظیم گذرواژه به نشانی رایانامه مرتبط با این حساب کاربری ارسال کردیم. از اینکه به ما کمک میکنید تا دادههای شما را ایمن نگه دارید متشکریم.",
|
||||
"account.locked.out.message.1": "حساب کاربری شما، به دلیل حفاظت، بهطور موقت قفل شده است. 30 دقیقه دیگر دوباره امتحان کنید.",
|
||||
"enterprise.login.btn.text": "اعتبار دانشکده یا شرکت",
|
||||
"username.or.email.format.validation.less.chars.message": "نام کاربری یا نشانی رایانامه حداقل باید 3 نویسه داشته باشد",
|
||||
"username.or.email.format.validation.less.chars.message": "نام کاربری یا نشانی رایانامه حداقل باید 2 نویسه داشته باشد",
|
||||
"email.validation.message": "نام کاربری یا رایانامه خود را وارد کنید",
|
||||
"password.validation.message": "معیارهای گذرواژه رعایت نشده است",
|
||||
"account.activation.success.message.title": "موفق شدید! شما حساب کاربری خود را فعال کردید.",
|
||||
@@ -177,4 +177,4 @@
|
||||
"reset.password.success.heading": "بازتنظیم گذرواژه تکمیل شد.",
|
||||
"reset.password.success": "گذرواژه شما بازتنظیم شد. وارد حساب کاربری خود شوید",
|
||||
"rate.limit.error": "به دلیل درخواستهای زیاد، خطایی روی داده است. لطفا بعد از مدتی دوباره امتحان کنید."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,7 +71,7 @@
|
||||
"non.compliant.password.message": "Votre mot de passe actuel ne répond pas aux nouvelles exigences de sécurité. Nous venons d'envoyer un message de réinitialisation de mot de passe à l'adresse courriel associée à ce compte. Merci de nous aider à protéger vos données.",
|
||||
"account.locked.out.message.1": "Pour protéger votre compte, il a été temporairement verrouillé. Réessayez dans 30 minutes.",
|
||||
"enterprise.login.btn.text": "Identifiants de la compagnie ou de l'école",
|
||||
"username.or.email.format.validation.less.chars.message": "Le nom d'utilisateur ou l'adresse courriel doit comporter au moins 3 caractères.",
|
||||
"username.or.email.format.validation.less.chars.message": "Le nom d'utilisateur ou l'adresse courriel doit comporter au moins 2 caractères.",
|
||||
"email.validation.message": "Entrez votre nom d'utilisateur ou votre adresse courriel",
|
||||
"password.validation.message": "Les critères de mot de passe n'ont pas été remplis",
|
||||
"account.activation.success.message.title": "Succès! Vous avez activé votre compte.",
|
||||
@@ -177,4 +177,4 @@
|
||||
"reset.password.success.heading": "Réinitialisation du mot de passe complétée.",
|
||||
"reset.password.success": "Votre mot de passe a été réinitialisé. Connectez-vous à votre compte.",
|
||||
"rate.limit.error": "Une erreur s'est produite en raison d'un trop grand nombre de demandes. Veuillez réessayer après un certain temps."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,7 +71,7 @@
|
||||
"non.compliant.password.message": "Votre mot de passe actuel ne répond pas aux nouvelles exigences de sécurité. Nous venons d'envoyer un message de réinitialisation de mot de passe à l'adresse courriel associée à ce compte. Merci de nous aider à protéger vos données.",
|
||||
"account.locked.out.message.1": "Pour protéger votre compte, il a été temporairement verrouillé. Réessayez dans 30 minutes.",
|
||||
"enterprise.login.btn.text": "Informations d'identification de la compagnie ou de l'école",
|
||||
"username.or.email.format.validation.less.chars.message": "Le nom d'utilisateur ou l'adresse courriel doit comporter au moins 3 caractères.",
|
||||
"username.or.email.format.validation.less.chars.message": "Le nom d'utilisateur ou l'adresse courriel doit comporter au moins 2 caractères.",
|
||||
"email.validation.message": "Entrez votre nom d'utilisateur ou votre adresse courriel",
|
||||
"password.validation.message": "Les critères de mot de passe n'ont pas été remplis",
|
||||
"account.activation.success.message.title": "Succès! Vous avez activé votre compte.",
|
||||
@@ -177,4 +177,4 @@
|
||||
"reset.password.success.heading": "Réinitialisation du mot de passe complétée.",
|
||||
"reset.password.success": "Votre mot de passe a été réinitialisé. Connectez-vous à votre compte.",
|
||||
"rate.limit.error": "Une erreur s'est produite en raison d'un trop grand nombre de demandes. Veuillez réessayer après un certain temps."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,7 +71,7 @@
|
||||
"non.compliant.password.message": "Your current password does not meet the new security requirements. We just sent a password-reset message to the email address associated with this account. Thank you for helping us keep your data safe.",
|
||||
"account.locked.out.message.1": "To protect your account, it's been temporarily locked. Try again in 30 minutes.",
|
||||
"enterprise.login.btn.text": "Company or school credentials",
|
||||
"username.or.email.format.validation.less.chars.message": "Username or email must have at least 3 characters.",
|
||||
"username.or.email.format.validation.less.chars.message": "Username or email must have at least 2 characters.",
|
||||
"email.validation.message": "Enter your username or email",
|
||||
"password.validation.message": "Password criteria has not been met",
|
||||
"account.activation.success.message.title": "Success! You have activated your account.",
|
||||
@@ -177,4 +177,4 @@
|
||||
"reset.password.success.heading": "Password reset complete.",
|
||||
"reset.password.success": "Your password has been reset. Sign in to your account.",
|
||||
"rate.limit.error": "An error has occurred because of too many requests. Please try again after some time."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,7 +71,7 @@
|
||||
"non.compliant.password.message": "Your current password does not meet the new security requirements. We just sent a password-reset message to the email address associated with this account. Thank you for helping us keep your data safe.",
|
||||
"account.locked.out.message.1": "To protect your account, it's been temporarily locked. Try again in 30 minutes.",
|
||||
"enterprise.login.btn.text": "Company or school credentials",
|
||||
"username.or.email.format.validation.less.chars.message": "Username or email must have at least 3 characters.",
|
||||
"username.or.email.format.validation.less.chars.message": "Username or email must have at least 2 characters.",
|
||||
"email.validation.message": "Enter your username or email",
|
||||
"password.validation.message": "Password criteria has not been met",
|
||||
"account.activation.success.message.title": "Success! You have activated your account.",
|
||||
@@ -177,4 +177,4 @@
|
||||
"reset.password.success.heading": "Password reset complete.",
|
||||
"reset.password.success": "Your password has been reset. Sign in to your account.",
|
||||
"rate.limit.error": "An error has occurred because of too many requests. Please try again after some time."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,7 +71,7 @@
|
||||
"non.compliant.password.message": "La tua password attuale non soddisfa i nuovi requisiti di sicurezza. Abbiamo appena inviato un messaggio di reimpostazione della password all'indirizzo e-mail associato a questo account. Grazie per averci aiutato a mantenere i tuoi dati al sicuro.",
|
||||
"account.locked.out.message.1": "Per proteggere il tuo account, è stato temporaneamente bloccato. Riprova tra 30 minuti.",
|
||||
"enterprise.login.btn.text": "Credenziali aziendali o scolastiche",
|
||||
"username.or.email.format.validation.less.chars.message": "Il nome utente o l'e-mail deve contenere almeno 3 caratteri.",
|
||||
"username.or.email.format.validation.less.chars.message": "Il nome utente o l'e-mail deve contenere almeno 2 caratteri.",
|
||||
"email.validation.message": "Inserisci il tuo nome utente o e-mail",
|
||||
"password.validation.message": "I criteri della password non sono stati soddisfatti",
|
||||
"account.activation.success.message.title": "Completato correttamente! Hai attivato il tuo account. ",
|
||||
@@ -177,4 +177,4 @@
|
||||
"reset.password.success.heading": "Ripristino della password completato.",
|
||||
"reset.password.success": "La tua password è stata resettata. Accedi al tuo account.",
|
||||
"rate.limit.error": "Si è verificato un errore dovuto alle troppe richieste. Prova di nuovo più tardi."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,7 +71,7 @@
|
||||
"non.compliant.password.message": "Your current password does not meet the new security requirements. We just sent a password-reset message to the email address associated with this account. Thank you for helping us keep your data safe.",
|
||||
"account.locked.out.message.1": "To protect your account, it's been temporarily locked. Try again in 30 minutes.",
|
||||
"enterprise.login.btn.text": "Company or school credentials",
|
||||
"username.or.email.format.validation.less.chars.message": "Username or email must have at least 3 characters.",
|
||||
"username.or.email.format.validation.less.chars.message": "Username or email must have at least 2 characters.",
|
||||
"email.validation.message": "Enter your username or email",
|
||||
"password.validation.message": "Password criteria has not been met",
|
||||
"account.activation.success.message.title": "Success! You have activated your account.",
|
||||
@@ -177,4 +177,4 @@
|
||||
"reset.password.success.heading": "Password reset complete.",
|
||||
"reset.password.success": "Your password has been reset. Sign in to your account.",
|
||||
"rate.limit.error": "An error has occurred because of too many requests. Please try again after some time."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,7 +71,7 @@
|
||||
"non.compliant.password.message": "A sua palavra-passe atual não satisfaz os novos requisitos de segurança. Acabámos de enviar uma mensagem de redefinição da palavra-passe para o endereço de email associado a esta conta. Obrigado por nos ajudar a manter os seus dados em segurança.",
|
||||
"account.locked.out.message.1": "Para proteger sua conta, esta foi temporariamente bloqueada. Tente novamente dentro de 30 minutos.",
|
||||
"enterprise.login.btn.text": "Credenciais da empresa ou escola",
|
||||
"username.or.email.format.validation.less.chars.message": "O nome de utilizador ou email deve ter pelo menos 3 carateres.",
|
||||
"username.or.email.format.validation.less.chars.message": "O nome de utilizador ou email deve ter pelo menos 2 carateres.",
|
||||
"email.validation.message": "Insira o seu nome de utilizador ou email",
|
||||
"password.validation.message": "Os critérios de palavra-passe não foram cumpridos",
|
||||
"account.activation.success.message.title": "Sucesso! Você ativou a sua conta.",
|
||||
@@ -177,4 +177,4 @@
|
||||
"reset.password.success.heading": "Redefinição de palavra-passe concluída",
|
||||
"reset.password.success": "A sua palavra-passe foi redefinida. Inicie sessão na sua conta.",
|
||||
"rate.limit.error": "Ocorreu um erro devido a demasiados pedidos. Por favor, tente novamente após algum tempo."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,7 +71,7 @@
|
||||
"non.compliant.password.message": "Your current password does not meet the new security requirements. We just sent a password-reset message to the email address associated with this account. Thank you for helping us keep your data safe.",
|
||||
"account.locked.out.message.1": "To protect your account, it's been temporarily locked. Try again in 30 minutes.",
|
||||
"enterprise.login.btn.text": "Company or school credentials",
|
||||
"username.or.email.format.validation.less.chars.message": "Username or email must have at least 3 characters.",
|
||||
"username.or.email.format.validation.less.chars.message": "Username or email must have at least 2 characters.",
|
||||
"email.validation.message": "Enter your username or email",
|
||||
"password.validation.message": "Password criteria has not been met",
|
||||
"account.activation.success.message.title": "Success! You have activated your account.",
|
||||
@@ -177,4 +177,4 @@
|
||||
"reset.password.success.heading": "Password reset complete.",
|
||||
"reset.password.success": "Your password has been reset. Sign in to your account.",
|
||||
"rate.limit.error": "An error has occurred because of too many requests. Please try again after some time."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,7 +71,7 @@
|
||||
"non.compliant.password.message": "Your current password does not meet the new security requirements. We just sent a password-reset message to the email address associated with this account. Thank you for helping us keep your data safe.",
|
||||
"account.locked.out.message.1": "To protect your account, it's been temporarily locked. Try again in 30 minutes.",
|
||||
"enterprise.login.btn.text": "Company or school credentials",
|
||||
"username.or.email.format.validation.less.chars.message": "Username or email must have at least 3 characters.",
|
||||
"username.or.email.format.validation.less.chars.message": "Username or email must have at least 2 characters.",
|
||||
"email.validation.message": "Enter your username or email",
|
||||
"password.validation.message": "Password criteria has not been met",
|
||||
"account.activation.success.message.title": "Success! You have activated your account.",
|
||||
@@ -177,4 +177,4 @@
|
||||
"reset.password.success.heading": "Password reset complete.",
|
||||
"reset.password.success": "Your password has been reset. Sign in to your account.",
|
||||
"rate.limit.error": "An error has occurred because of too many requests. Please try again after some time."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,7 +71,7 @@
|
||||
"non.compliant.password.message": "您当前的密码不符合新的安全要求。我们刚刚向与此帐户关联的电子邮件地址发送了密码重置邮件。感谢您帮助我们保护您的数据安全。",
|
||||
"account.locked.out.message.1": "为了保护您的帐户,它已被暂时锁定。请在 30 分钟后重试。",
|
||||
"enterprise.login.btn.text": "单位或学校证书",
|
||||
"username.or.email.format.validation.less.chars.message": "用户名或电子邮件必须至少包含 3 个字符。",
|
||||
"username.or.email.format.validation.less.chars.message": "用户名或电子邮件必须至少包含 2 个字符。",
|
||||
"email.validation.message": "输入您的用户名或电子邮件",
|
||||
"password.validation.message": "未满足密码条件",
|
||||
"account.activation.success.message.title": "成功!您已激活您的帐户。",
|
||||
@@ -177,4 +177,4 @@
|
||||
"reset.password.success.heading": "密码重置完成。",
|
||||
"reset.password.success": "您的密码已重置。登录到您的帐户。",
|
||||
"rate.limit.error": "由于请求过多而发生错误。请稍后重试。"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -147,7 +147,7 @@ class LoginPage extends React.Component {
|
||||
|
||||
if (email === '') {
|
||||
errors.emailOrUsername = this.props.intl.formatMessage(messages['email.validation.message']);
|
||||
} else if (email.length < 3) {
|
||||
} else if (email.length < 2) {
|
||||
errors.emailOrUsername = this.props.intl.formatMessage(messages['username.or.email.format.validation.less.chars.message']);
|
||||
} else {
|
||||
errors.emailOrUsername = '';
|
||||
@@ -166,6 +166,7 @@ class LoginPage extends React.Component {
|
||||
const isInstitutionAuthActive = !!secondaryProviders.length && !currentProvider;
|
||||
const isSocialAuthActive = !!providers.length && !currentProvider;
|
||||
const isEnterpriseLoginDisabled = getConfig().DISABLE_ENTERPRISE_LOGIN;
|
||||
const isThirdPartyAuthActive = isSocialAuthActive || (isEnterpriseLoginDisabled && isInstitutionAuthActive);
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -183,7 +184,7 @@ class LoginPage extends React.Component {
|
||||
</Hyperlink>
|
||||
)}
|
||||
|
||||
{thirdPartyAuthApiStatus === PENDING_STATE ? (
|
||||
{thirdPartyAuthApiStatus === PENDING_STATE && isThirdPartyAuthActive ? (
|
||||
<Skeleton className="tpa-skeleton mb-3" height={30} count={2} />
|
||||
) : (
|
||||
<>
|
||||
|
||||
@@ -71,8 +71,8 @@ const messages = defineMessages({
|
||||
},
|
||||
'username.or.email.format.validation.less.chars.message': {
|
||||
id: 'username.or.email.format.validation.less.chars.message',
|
||||
defaultMessage: 'Username or email must have at least 3 characters.',
|
||||
description: 'Validation message that appears when username or email address is less than 3 characters',
|
||||
defaultMessage: 'Username or email must have at least 2 characters.',
|
||||
description: 'Validation message that appears when username or email address is less than 2 characters',
|
||||
},
|
||||
'email.validation.message': {
|
||||
id: 'email.validation.message',
|
||||
|
||||
@@ -128,14 +128,14 @@ describe('LoginPage', () => {
|
||||
expect(store.dispatch).toHaveBeenCalledWith(loginRequestFailure({ errorCode: 'invalid-form' }));
|
||||
});
|
||||
|
||||
it('should match state for invalid email (less than 3 characters), on form submission', () => {
|
||||
const errorState = { emailOrUsername: 'Username or email must have at least 3 characters.', password: '' };
|
||||
it('should match state for invalid email (less than 2 characters), on form submission', () => {
|
||||
const errorState = { emailOrUsername: 'Username or email must have at least 2 characters.', password: '' };
|
||||
store.dispatch = jest.fn(store.dispatch);
|
||||
|
||||
const loginPage = (mount(reduxWrapper(<IntlLoginPage {...props} />))).find('LoginPage');
|
||||
|
||||
loginPage.find('input#password').simulate('change', { target: { value: 'test', name: 'password' } });
|
||||
loginPage.find('input#emailOrUsername').simulate('change', { target: { value: 'te', name: 'email' } });
|
||||
loginPage.find('input#emailOrUsername').simulate('change', { target: { value: 't', name: 'email' } });
|
||||
loginPage.find('button.btn-brand').simulate('click');
|
||||
|
||||
expect(loginPage.state('errors')).toEqual(errorState);
|
||||
|
||||
@@ -64,7 +64,10 @@ const Logistration = (props) => {
|
||||
setInstitutionLogin(!institutionLogin);
|
||||
};
|
||||
|
||||
const handleOnSelect = (tabKey) => {
|
||||
const handleOnSelect = (tabKey, currentTab) => {
|
||||
if (tabKey === currentTab) {
|
||||
return;
|
||||
}
|
||||
sendTrackEvent(`edx.bi.${tabKey.replace('/', '')}_form.toggled`, { category: 'user-engagement' });
|
||||
props.clearThirdPartyAuthContextErrorMessage();
|
||||
if (tabKey === LOGIN_PAGE) {
|
||||
@@ -117,7 +120,7 @@ const Logistration = (props) => {
|
||||
</Tabs>
|
||||
)
|
||||
: (!isValidTpaHint() && (
|
||||
<Tabs defaultActiveKey={selectedPage} id="controlled-tab" onSelect={handleOnSelect}>
|
||||
<Tabs defaultActiveKey={selectedPage} id="controlled-tab" onSelect={(tabKey) => handleOnSelect(tabKey, selectedPage)}>
|
||||
<Tab title={formatMessage(messages['logistration.register'])} eventKey={REGISTER_PAGE} />
|
||||
<Tab title={formatMessage(messages['logistration.sign.in'])} eventKey={LOGIN_PAGE} />
|
||||
</Tabs>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useState } from 'react';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
|
||||
import { useIntl } from '@edx/frontend-platform/i18n';
|
||||
@@ -9,9 +9,9 @@ import PropTypes from 'prop-types';
|
||||
import validateEmail from './validator';
|
||||
import { FormGroup } from '../../../common-components';
|
||||
import {
|
||||
backupRegistrationFormBegin,
|
||||
clearRegistrationBackendError,
|
||||
fetchRealtimeValidations,
|
||||
setEmailSuggestionInStore,
|
||||
} from '../../data/actions';
|
||||
import messages from '../../messages';
|
||||
|
||||
@@ -44,6 +44,10 @@ const EmailField = (props) => {
|
||||
|
||||
const [emailSuggestion, setEmailSuggestion] = useState({ ...backedUpFormData?.emailSuggestion });
|
||||
|
||||
useEffect(() => {
|
||||
setEmailSuggestion(backedUpFormData.emailSuggestion);
|
||||
}, [backedUpFormData.emailSuggestion]);
|
||||
|
||||
const handleOnBlur = (e) => {
|
||||
const { value } = e.target;
|
||||
const { fieldError, confirmEmailError, suggestion } = validateEmail(value, confirmEmailValue, formatMessage);
|
||||
@@ -52,10 +56,7 @@ const EmailField = (props) => {
|
||||
handleErrorChange('confirm_email', confirmEmailError);
|
||||
}
|
||||
|
||||
dispatch(backupRegistrationFormBegin({
|
||||
...backedUpFormData,
|
||||
emailSuggestion: { ...suggestion },
|
||||
}));
|
||||
dispatch(setEmailSuggestionInStore(suggestion));
|
||||
setEmailSuggestion(suggestion);
|
||||
|
||||
if (fieldError) {
|
||||
|
||||
@@ -46,7 +46,14 @@ describe('EmailField', () => {
|
||||
);
|
||||
|
||||
const initialState = {
|
||||
register: {},
|
||||
register: {
|
||||
registrationFormData: {
|
||||
emailSuggestion: {
|
||||
suggestion: 'example@gmail.com',
|
||||
type: 'warning',
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
|
||||
@@ -91,7 +91,7 @@ export const validateEmailAddress = (value, username, domainName) => {
|
||||
const validateEmail = (value, confirmEmailValue, formatMessage) => {
|
||||
let fieldError = '';
|
||||
let confirmEmailError = '';
|
||||
let emailSuggestion = {};
|
||||
let emailSuggestion = { suggestion: '', type: '' };
|
||||
|
||||
if (!value) {
|
||||
fieldError = formatMessage(messages['empty.email.field.error']);
|
||||
|
||||
@@ -4,7 +4,7 @@ export const INVALID_NAME_REGEX = /[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{
|
||||
export const urlRegex = new RegExp(INVALID_NAME_REGEX);
|
||||
|
||||
const validateName = (value, formatMessage) => {
|
||||
let fieldError;
|
||||
let fieldError = '';
|
||||
if (!value.trim()) {
|
||||
fieldError = formatMessage(messages['empty.name.field.error']);
|
||||
} else if (value && value.match(urlRegex)) {
|
||||
|
||||
@@ -100,7 +100,7 @@ const UsernameField = (props) => {
|
||||
};
|
||||
|
||||
const suggestedUsernames = () => (
|
||||
<div className={className}>
|
||||
<div className={className} role="listbox">
|
||||
<span className="text-gray username-suggestion--label">{formatMessage(messages['registration.username.suggestion.label'])}</span>
|
||||
<div className="username-scroll-suggested--form-field">
|
||||
{usernameSuggestions.map((username, index) => (
|
||||
@@ -111,7 +111,9 @@ const UsernameField = (props) => {
|
||||
className="username-suggestions--chip data-hj-suppress"
|
||||
autoComplete={props.autoComplete}
|
||||
key={`suggestion-${index.toString()}`}
|
||||
tabIndex={0}
|
||||
onClick={(e) => handleSuggestionClick(e, username)}
|
||||
role="option"
|
||||
>
|
||||
{username}
|
||||
</Button>
|
||||
@@ -122,7 +124,7 @@ const UsernameField = (props) => {
|
||||
);
|
||||
|
||||
if (usernameSuggestions.length > 0 && errorMessage && value === ' ') {
|
||||
className = 'username-suggestions__error';
|
||||
className = 'username-suggestions';
|
||||
iconButton = <IconButton src={Close} iconAs={Icon} alt="Close" onClick={() => handleUsernameSuggestionClose()} variant="black" size="sm" className="username-suggestions__close__button" />;
|
||||
suggestedUsernameDiv = suggestedUsernames();
|
||||
} else if (usernameSuggestions.length > 0 && value === ' ') {
|
||||
@@ -133,14 +135,15 @@ const UsernameField = (props) => {
|
||||
suggestedUsernameDiv = suggestedUsernames();
|
||||
}
|
||||
return (
|
||||
<FormGroup
|
||||
{...props}
|
||||
handleChange={handleOnChange}
|
||||
handleFocus={handleOnFocus}
|
||||
handleBlur={handleOnBlur}
|
||||
>
|
||||
<div className="username__form-group-wrapper">
|
||||
{suggestedUsernameDiv}
|
||||
</FormGroup>
|
||||
<FormGroup
|
||||
{...props}
|
||||
handleChange={handleOnChange}
|
||||
handleFocus={handleOnFocus}
|
||||
handleBlur={handleOnBlur}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ import {
|
||||
backupRegistrationFormBegin,
|
||||
clearRegistrationBackendError,
|
||||
registerNewUser,
|
||||
setEmailSuggestionInStore,
|
||||
setUserPipelineDataLoaded,
|
||||
} from './data/actions';
|
||||
import {
|
||||
@@ -185,8 +186,8 @@ const RegistrationPage = (props) => {
|
||||
const value = event.target.type === 'checkbox' ? event.target.checked : event.target.value;
|
||||
if (registrationError[name]) {
|
||||
dispatch(clearRegistrationBackendError(name));
|
||||
setErrors(prevErrors => ({ ...prevErrors, [name]: '' }));
|
||||
}
|
||||
setErrors(prevErrors => ({ ...prevErrors, [name]: '' }));
|
||||
setFormFields(prevState => ({ ...prevState, [name]: value }));
|
||||
};
|
||||
|
||||
@@ -220,7 +221,7 @@ const RegistrationPage = (props) => {
|
||||
}
|
||||
|
||||
// Validating form data before submitting
|
||||
const { isValid, fieldErrors } = isFormValid(
|
||||
const { isValid, fieldErrors, emailSuggestion } = isFormValid(
|
||||
payload,
|
||||
registrationEmbedded ? temporaryErrors : errors,
|
||||
configurableFormFields,
|
||||
@@ -228,6 +229,7 @@ const RegistrationPage = (props) => {
|
||||
formatMessage,
|
||||
);
|
||||
setErrors({ ...fieldErrors });
|
||||
dispatch(setEmailSuggestionInStore(emailSuggestion));
|
||||
|
||||
// returning if not valid
|
||||
if (!isValid) {
|
||||
|
||||
@@ -235,6 +235,53 @@ describe('RegistrationPage', () => {
|
||||
expect(store.dispatch).toHaveBeenCalledWith(registerNewUser({ ...formPayload, country: 'PK' }));
|
||||
});
|
||||
|
||||
it('should display an error when form is submitted with an invalid email', () => {
|
||||
jest.spyOn(global.Date, 'now').mockImplementation(() => 0);
|
||||
const emailError = 'Enter a valid email address';
|
||||
|
||||
const formPayload = {
|
||||
name: 'Petro',
|
||||
username: 'petro_qa',
|
||||
email: 'petro @example.com',
|
||||
password: 'password1',
|
||||
country: 'Ukraine',
|
||||
honor_code: true,
|
||||
totalRegistrationTime: 0,
|
||||
};
|
||||
|
||||
store.dispatch = jest.fn(store.dispatch);
|
||||
const registrationPage = mount(routerWrapper(reduxWrapper(<IntlRegistrationPage {...props} />)));
|
||||
populateRequiredFields(registrationPage, formPayload, true);
|
||||
registrationPage.find('button.btn-brand').simulate('click');
|
||||
expect(
|
||||
registrationPage.find('div[feedback-for="email"]').text(),
|
||||
).toEqual(emailError);
|
||||
});
|
||||
|
||||
it('should display an error when form is submitted with an invalid username', () => {
|
||||
jest.spyOn(global.Date, 'now').mockImplementation(() => 0);
|
||||
const usernameError = 'Usernames can only contain letters (A-Z, a-z), numerals (0-9), '
|
||||
+ 'underscores (_), and hyphens (-). Usernames cannot contain spaces';
|
||||
|
||||
const formPayload = {
|
||||
name: 'Petro',
|
||||
username: 'petro qa',
|
||||
email: 'petro@example.com',
|
||||
password: 'password1',
|
||||
country: 'Ukraine',
|
||||
honor_code: true,
|
||||
totalRegistrationTime: 0,
|
||||
};
|
||||
|
||||
store.dispatch = jest.fn(store.dispatch);
|
||||
const registrationPage = mount(routerWrapper(reduxWrapper(<IntlRegistrationPage {...props} />)));
|
||||
populateRequiredFields(registrationPage, formPayload, true);
|
||||
registrationPage.find('button.btn-brand').simulate('click');
|
||||
expect(
|
||||
registrationPage.find('div[feedback-for="username"]').text(),
|
||||
).toEqual(usernameError);
|
||||
});
|
||||
|
||||
it('should submit form with marketing email opt in value', () => {
|
||||
mergeConfig({
|
||||
MARKETING_EMAILS_OPT_IN: 'true',
|
||||
|
||||
@@ -11,6 +11,7 @@ import configureStore from 'redux-mock-store';
|
||||
|
||||
import ConfigurableRegistrationForm from './ConfigurableRegistrationForm';
|
||||
import { FIELDS } from '../data/constants';
|
||||
import RegistrationPage from '../RegistrationPage';
|
||||
|
||||
jest.mock('@edx/frontend-platform/analytics', () => ({
|
||||
sendPageEvent: jest.fn(),
|
||||
@@ -22,7 +23,20 @@ jest.mock('@edx/frontend-platform/i18n', () => ({
|
||||
}));
|
||||
|
||||
const IntlConfigurableRegistrationForm = injectIntl(ConfigurableRegistrationForm);
|
||||
const IntlRegistrationPage = injectIntl(RegistrationPage);
|
||||
const mockStore = configureStore();
|
||||
const populateRequiredFields = (registrationPage, payload, isThirdPartyAuth = false) => {
|
||||
registrationPage.find('input#name').simulate('change', { target: { value: payload.name, name: 'name' } });
|
||||
registrationPage.find('input#username').simulate('change', { target: { value: payload.username, name: 'username' } });
|
||||
registrationPage.find('input#email').simulate('change', { target: { value: payload.email, name: 'email' } });
|
||||
|
||||
registrationPage.find('input[name="country"]').simulate('change', { target: { value: payload.country, name: 'country' } });
|
||||
registrationPage.find('input[name="country"]').simulate('blur', { target: { value: payload.country, name: 'country' } });
|
||||
|
||||
if (!isThirdPartyAuth) {
|
||||
registrationPage.find('input#password').simulate('change', { target: { value: payload.password, name: 'password' } });
|
||||
}
|
||||
};
|
||||
|
||||
jest.mock('react-router-dom', () => {
|
||||
const mockNavigation = jest.fn();
|
||||
@@ -182,5 +196,52 @@ describe('ConfigurableRegistrationForm', () => {
|
||||
[FIELDS.TERMS_OF_SERVICE]: true,
|
||||
});
|
||||
});
|
||||
|
||||
it('should show error if email and confirm email fields do not match on submit click', () => {
|
||||
const formPayload = {
|
||||
name: 'Petro',
|
||||
username: 'petro_qa',
|
||||
email: 'petro@example.com',
|
||||
password: 'password1',
|
||||
country: 'Ukraine',
|
||||
honor_code: true,
|
||||
totalRegistrationTime: 0,
|
||||
};
|
||||
|
||||
store = mockStore({
|
||||
...initialState,
|
||||
commonComponents: {
|
||||
...initialState.commonComponents,
|
||||
fieldDescriptions: {
|
||||
confirm_email: {
|
||||
name: 'confirm_email', type: 'text', label: 'Confirm Email',
|
||||
},
|
||||
country: { name: 'country' },
|
||||
},
|
||||
},
|
||||
});
|
||||
const registrationPage = mount(routerWrapper(reduxWrapper(
|
||||
<IntlRegistrationPage {...props} />,
|
||||
)));
|
||||
|
||||
populateRequiredFields(registrationPage, formPayload, true);
|
||||
registrationPage.find('input#confirm_email').simulate(
|
||||
'change', { target: { value: 'test2@gmail.com', name: 'confirm_email' } },
|
||||
);
|
||||
|
||||
const button = registrationPage.find('button.btn-brand');
|
||||
button.simulate('click');
|
||||
|
||||
registrationPage.update();
|
||||
|
||||
const confirmEmailErrorElement = registrationPage.find('div#confirm_email-error');
|
||||
expect(confirmEmailErrorElement.text()).toEqual('The email addresses do not match.');
|
||||
|
||||
const validationErrors = registrationPage.find('#validation-errors');
|
||||
const firstValidationErrorText = validationErrors.first().text();
|
||||
expect(firstValidationErrorText).toContain(
|
||||
"We couldn't create your account.Please check your responses and try again.",
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -25,6 +25,7 @@ const ThirdPartyAuth = (props) => {
|
||||
const isInstitutionAuthActive = !!secondaryProviders.length && !currentProvider;
|
||||
const isSocialAuthActive = !!providers.length && !currentProvider;
|
||||
const isEnterpriseLoginDisabled = getConfig().DISABLE_ENTERPRISE_LOGIN;
|
||||
const isThirdPartyAuthActive = isSocialAuthActive || (isEnterpriseLoginDisabled && isInstitutionAuthActive);
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -34,7 +35,7 @@ const ThirdPartyAuth = (props) => {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{thirdPartyAuthApiStatus === PENDING_STATE ? (
|
||||
{thirdPartyAuthApiStatus === PENDING_STATE && isThirdPartyAuthActive ? (
|
||||
<Skeleton className="tpa-skeleton" height={36} count={2} />
|
||||
) : (
|
||||
<>
|
||||
|
||||
@@ -6,6 +6,7 @@ export const REGISTER_NEW_USER = new AsyncActionType('REGISTRATION', 'REGISTER_N
|
||||
export const REGISTER_CLEAR_USERNAME_SUGGESTIONS = 'REGISTRATION_CLEAR_USERNAME_SUGGESTIONS';
|
||||
export const REGISTRATION_CLEAR_BACKEND_ERROR = 'REGISTRATION_CLEAR_BACKEND_ERROR';
|
||||
export const REGISTER_SET_COUNTRY_CODE = 'REGISTER_SET_COUNTRY_CODE';
|
||||
export const REGISTER_SET_EMAIL_SUGGESTIONS = 'REGISTER_SET_EMAIL_SUGGESTIONS';
|
||||
export const REGISTER_SET_USER_PIPELINE_DATA_LOADED = 'REGISTER_SET_USER_PIPELINE_DATA_LOADED';
|
||||
|
||||
// Backup registration form
|
||||
@@ -37,6 +38,12 @@ export const fetchRealtimeValidationsFailure = () => ({
|
||||
type: REGISTER_FORM_VALIDATIONS.FAILURE,
|
||||
});
|
||||
|
||||
// Set email field frontend validations
|
||||
export const setEmailSuggestionInStore = (emailSuggestion) => ({
|
||||
type: REGISTER_SET_EMAIL_SUGGESTIONS,
|
||||
payload: { emailSuggestion },
|
||||
});
|
||||
|
||||
// Register
|
||||
export const registerNewUser = registrationInfo => ({
|
||||
type: REGISTER_NEW_USER.BASE,
|
||||
|
||||
@@ -3,7 +3,9 @@ import {
|
||||
REGISTER_CLEAR_USERNAME_SUGGESTIONS,
|
||||
REGISTER_FORM_VALIDATIONS,
|
||||
REGISTER_NEW_USER,
|
||||
REGISTER_SET_COUNTRY_CODE, REGISTER_SET_USER_PIPELINE_DATA_LOADED,
|
||||
REGISTER_SET_COUNTRY_CODE,
|
||||
REGISTER_SET_EMAIL_SUGGESTIONS,
|
||||
REGISTER_SET_USER_PIPELINE_DATA_LOADED,
|
||||
REGISTRATION_CLEAR_BACKEND_ERROR,
|
||||
} from './actions';
|
||||
import {
|
||||
@@ -119,6 +121,15 @@ const reducer = (state = defaultState, action = {}) => {
|
||||
userPipelineDataLoaded: value,
|
||||
};
|
||||
}
|
||||
case REGISTER_SET_EMAIL_SUGGESTIONS: {
|
||||
return {
|
||||
...state,
|
||||
registrationFormData: {
|
||||
...state.registrationFormData,
|
||||
emailSuggestion: action.payload.emailSuggestion,
|
||||
},
|
||||
};
|
||||
}
|
||||
default:
|
||||
return {
|
||||
...state,
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
import { camelCaseObject } from '@edx/frontend-platform';
|
||||
import { logError, logInfo } from '@edx/frontend-platform/logging';
|
||||
import { call, put, takeEvery } from 'redux-saga/effects';
|
||||
import {
|
||||
call, put, race, take, takeEvery,
|
||||
} from 'redux-saga/effects';
|
||||
|
||||
import {
|
||||
fetchRealtimeValidationsBegin,
|
||||
fetchRealtimeValidationsFailure,
|
||||
fetchRealtimeValidationsSuccess,
|
||||
REGISTER_CLEAR_USERNAME_SUGGESTIONS,
|
||||
REGISTER_FORM_VALIDATIONS,
|
||||
REGISTER_NEW_USER,
|
||||
registerNewUserBegin,
|
||||
@@ -41,9 +44,15 @@ export function* handleNewUserRegistration(action) {
|
||||
export function* fetchRealtimeValidations(action) {
|
||||
try {
|
||||
yield put(fetchRealtimeValidationsBegin());
|
||||
const { fieldValidations } = yield call(getFieldsValidations, action.payload.formPayload);
|
||||
|
||||
yield put(fetchRealtimeValidationsSuccess(camelCaseObject(fieldValidations)));
|
||||
const { response } = yield race({
|
||||
response: call(getFieldsValidations, action.payload.formPayload),
|
||||
cancel: take(REGISTER_CLEAR_USERNAME_SUGGESTIONS),
|
||||
});
|
||||
|
||||
if (response) {
|
||||
yield put(fetchRealtimeValidationsSuccess(camelCaseObject(response.fieldValidations)));
|
||||
}
|
||||
} catch (e) {
|
||||
if (e.response && e.response.status === 403) {
|
||||
yield put(fetchRealtimeValidationsFailure());
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
REGISTER_FORM_VALIDATIONS,
|
||||
REGISTER_NEW_USER,
|
||||
REGISTER_SET_COUNTRY_CODE,
|
||||
REGISTER_SET_EMAIL_SUGGESTIONS,
|
||||
REGISTER_SET_USER_PIPELINE_DATA_LOADED,
|
||||
REGISTRATION_CLEAR_BACKEND_ERROR,
|
||||
} from '../actions';
|
||||
@@ -64,6 +65,29 @@ describe('Registration Reducer Tests', () => {
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
it('should set email suggestions', () => {
|
||||
const emailSuggestion = {
|
||||
type: 'test type',
|
||||
suggestion: 'test suggestion',
|
||||
};
|
||||
const action = {
|
||||
type: REGISTER_SET_EMAIL_SUGGESTIONS,
|
||||
payload: { emailSuggestion },
|
||||
};
|
||||
|
||||
expect(reducer(defaultState, action)).toEqual(
|
||||
{
|
||||
...defaultState,
|
||||
registrationFormData: {
|
||||
...defaultState.registrationFormData,
|
||||
emailSuggestion: {
|
||||
type: 'test type', suggestion: 'test suggestion',
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('should set redirect url dashboard on registration success action', () => {
|
||||
const payload = {
|
||||
redirectUrl: `${getConfig().BASE_URL}${DEFAULT_REDIRECT_URL}`,
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
import { snakeCaseObject } from '@edx/frontend-platform';
|
||||
import { getConfig, snakeCaseObject } from '@edx/frontend-platform';
|
||||
|
||||
import { LETTER_REGEX, NUMBER_REGEX } from '../../data/constants';
|
||||
import messages from '../messages';
|
||||
import validateEmail from '../RegistrationFields/EmailField/validator';
|
||||
import validateName from '../RegistrationFields/NameField/validator';
|
||||
import validateUsername from '../RegistrationFields/UsernameField/validator';
|
||||
|
||||
/**
|
||||
* It validates the password field value
|
||||
@@ -35,32 +38,57 @@ export const isFormValid = (
|
||||
) => {
|
||||
const fieldErrors = { ...errors };
|
||||
let isValid = true;
|
||||
let emailSuggestion = { suggestion: '', type: '' };
|
||||
Object.keys(payload).forEach(key => {
|
||||
if (!payload[key]) {
|
||||
fieldErrors[key] = formatMessage(messages[`empty.${key}.field.error`]);
|
||||
}
|
||||
if (fieldErrors[key]) {
|
||||
isValid = false;
|
||||
switch (key) {
|
||||
case 'name':
|
||||
fieldErrors.name = validateName(payload.name, formatMessage);
|
||||
if (fieldErrors.name) { isValid = false; }
|
||||
break;
|
||||
case 'email': {
|
||||
const {
|
||||
fieldError, confirmEmailError, suggestion,
|
||||
} = validateEmail(payload.email, configurableFormFields?.confirm_email, formatMessage);
|
||||
if (fieldError) {
|
||||
fieldErrors.email = fieldError;
|
||||
isValid = false;
|
||||
}
|
||||
if (confirmEmailError) {
|
||||
fieldErrors.confirm_email = confirmEmailError;
|
||||
isValid = false;
|
||||
}
|
||||
emailSuggestion = suggestion;
|
||||
break;
|
||||
}
|
||||
case 'username':
|
||||
fieldErrors.username = validateUsername(payload.username, formatMessage);
|
||||
if (fieldErrors.username) { isValid = false; }
|
||||
break;
|
||||
case 'password':
|
||||
fieldErrors.password = validatePasswordField(payload.password, formatMessage);
|
||||
if (fieldErrors.password) { isValid = false; }
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
if (!configurableFormFields?.country?.displayValue) {
|
||||
fieldErrors.country = formatMessage(messages['empty.country.field.error']);
|
||||
isValid = false;
|
||||
if (getConfig().SHOW_CONFIGURABLE_EDX_FIELDS) {
|
||||
if (!configurableFormFields?.country?.displayValue) {
|
||||
fieldErrors.country = formatMessage(messages['empty.country.field.error']);
|
||||
isValid = false;
|
||||
}
|
||||
}
|
||||
|
||||
Object.keys(fieldDescriptions).forEach(key => {
|
||||
if (key === 'country' && !configurableFormFields.country.displayValue) {
|
||||
fieldErrors[key] = formatMessage(messages['empty.country.field.error']);
|
||||
} else if (!configurableFormFields[key]) {
|
||||
fieldErrors[key] = fieldDescriptions[key].error_message;
|
||||
}
|
||||
if (fieldErrors[key]) {
|
||||
isValid = false;
|
||||
}
|
||||
if (fieldErrors[key]) { isValid = false; }
|
||||
});
|
||||
|
||||
return { isValid, fieldErrors };
|
||||
return { isValid, fieldErrors, emailSuggestion };
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -38,6 +38,11 @@
|
||||
max-width: 320px;
|
||||
}
|
||||
|
||||
.main-heading {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.complete-your-profile {
|
||||
font-weight: 700;
|
||||
line-height: 1;
|
||||
|
||||
@@ -65,10 +65,15 @@
|
||||
margin-right: 0.25rem;
|
||||
}
|
||||
|
||||
.username-suggestions {
|
||||
.username__form-group-wrapper {
|
||||
position: relative;
|
||||
margin-top: -2.5rem;
|
||||
margin-left: 15px;
|
||||
}
|
||||
|
||||
.username-suggestions {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
padding-left: 15px;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
.username-suggestions__close__button {
|
||||
@@ -76,13 +81,6 @@
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.username-suggestions__error {
|
||||
position: relative;
|
||||
margin-top: -13.7%;
|
||||
margin-bottom: 11%;
|
||||
margin-left: 15px;
|
||||
}
|
||||
|
||||
.username-scroll-suggested--form-field {
|
||||
width: 20rem;
|
||||
white-space: nowrap;
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
// ----------------------------
|
||||
// #COLORS
|
||||
// ----------------------------
|
||||
$font-blue: #126f9a;
|
||||
$white: #FFFFFF;
|
||||
|
||||
// social platforms
|
||||
@@ -105,10 +104,10 @@ $elevation-level-2-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.15);
|
||||
font-size: 14px;
|
||||
|
||||
background-color: $white;
|
||||
border: 1px solid $font-blue;
|
||||
border: 1px solid $primary;
|
||||
width: 224px;
|
||||
height: 36px;
|
||||
color: $font-blue;
|
||||
color: $primary;
|
||||
|
||||
.btn-tpa__image-icon{
|
||||
background-color: transparent;
|
||||
@@ -133,7 +132,7 @@ $elevation-level-2-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
.btn-tpa__font-container {
|
||||
background-color: $font-blue;
|
||||
background-color: $primary;
|
||||
color: $white;
|
||||
font-size: 11px;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user