diff --git a/frontend/src/ts/controllers/account-controller.ts b/frontend/src/ts/controllers/account-controller.ts index 3c5c0f205..456391305 100644 --- a/frontend/src/ts/controllers/account-controller.ts +++ b/frontend/src/ts/controllers/account-controller.ts @@ -527,6 +527,18 @@ async function signUp(): Promise { return; } + if ( + !email.match( + /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/ + ) + ) { + Notifications.add("Invalid email", 0, 3); + LoginPage.hidePreloader(); + $(".pageLogin .button").removeClass("disabled"); + $(".pageLogin input").prop("disabled", false); + return; + } + if (email !== emailVerify) { Notifications.add("Emails do not match", 0, 3); LoginPage.hidePreloader(); @@ -535,6 +547,30 @@ async function signUp(): Promise { return; } + // Force user to use a capital letter, number, special character when setting up an account and changing password + if (password.length < 8) { + Notifications.add("Password must be at least 8 characters", 0, 3); + LoginPage.hidePreloader(); + $(".pageLogin .button").removeClass("disabled"); + $(".pageLogin input").prop("disabled", false); + return; + } + + const hasCapital = password.match(/[A-Z]/); + const hasNumber = password.match(/[\d]/); + const hasSpecial = password.match(/[!@#$%^&*()_+\-=[\]{};':"\\|,.<>/?]/); + if (!hasCapital || !hasNumber || !hasSpecial) { + Notifications.add( + "Password must contain at least one capital letter, number, and special character", + 0, + 3 + ); + LoginPage.hidePreloader(); + $(".pageLogin .button").removeClass("disabled"); + $(".pageLogin input").prop("disabled", false); + return; + } + if (password !== passwordVerify) { Notifications.add("Passwords do not match", 0, 3); LoginPage.hidePreloader(); diff --git a/frontend/src/ts/pages/login.ts b/frontend/src/ts/pages/login.ts index 8a4d51e98..6a73c6896 100644 --- a/frontend/src/ts/pages/login.ts +++ b/frontend/src/ts/pages/login.ts @@ -22,29 +22,6 @@ export function hidePreloader(): void { $(".pageLogin .preloader").addClass("hidden"); } -const nameIndicator = new InputIndicator( - $(".page.pageLogin .register.side .username.inputAndIndicator"), - { - available: { - icon: "fa-check", - level: 1, - }, - unavailable: { - icon: "fa-times", - level: -1, - }, - taken: { - icon: "fa-times", - level: -1, - }, - checking: { - icon: "fa-circle-notch", - spinIcon: true, - level: 0, - }, - } -); - const checkNameDebounced = debounce(1000, async () => { const val = $( ".page.pageLogin .register.side .usernameInput" @@ -76,6 +53,142 @@ const checkNameDebounced = debounce(1000, async () => { } }); +const checkEmailsMatch = (): void => { + const email = $(".page.pageLogin .register.side .emailInput").val(); + const verifyEmail = $( + ".page.pageLogin .register.side .verifyEmailInput" + ).val(); + verifyEmailIndicator.show(email === verifyEmail ? "match" : "mismatch"); +}; + +const checkEmail = (): void => { + const emailRegex = + /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; + + const email = $(".page.pageLogin .register.side .emailInput").val() as string; + + emailIndicator.show(emailRegex.test(email) ? "valid" : "invalid"); +}; + +const checkPassword = (): void => { + passwordIndicator.show("good"); + const password = $( + ".page.pageLogin .register.side .passwordInput" + ).val() as string; + + // Force user to use a capital letter, number, special character when setting up an account and changing password + if (password.length < 8) { + passwordIndicator.show("short", "Password must be at least 8 characters"); + return; + } + + const hasCapital = password.match(/[A-Z]/); + const hasNumber = password.match(/[\d]/); + const hasSpecial = password.match(/[!@#$%^&*()_+\-=[\]{};':"\\|,.<>/?]/); + if (!hasCapital || !hasNumber || !hasSpecial) { + passwordIndicator.show( + "weak", + "Password must contain at least one capital letter, number, and special character" + ); + return; + } + + passwordIndicator.show("good", "Password is good"); +}; + +const checkPasswordsMatch = (): void => { + const password = $(".page.pageLogin .register.side .passwordInput").val(); + const verifyPassword = $( + ".page.pageLogin .register.side .verifyPasswordInput" + ).val(); + verifyPasswordIndicator.show( + password === verifyPassword ? "match" : "mismatch" + ); +}; + +const nameIndicator = new InputIndicator( + $(".page.pageLogin .register.side .username.inputAndIndicator"), + { + available: { + icon: "fa-check", + level: 1, + }, + unavailable: { + icon: "fa-times", + level: -1, + }, + taken: { + icon: "fa-times", + level: -1, + }, + checking: { + icon: "fa-circle-notch", + spinIcon: true, + level: 0, + }, + } +); + +const emailIndicator = new InputIndicator( + $(".page.pageLogin .register.side .email.inputAndIndicator"), + { + valid: { + icon: "fa-check", + level: 1, + }, + invalid: { + icon: "fa-times", + level: -1, + }, + } +); + +const verifyEmailIndicator = new InputIndicator( + $(".page.pageLogin .register.side .verifyEmail.inputAndIndicator"), + { + match: { + icon: "fa-check", + level: 1, + }, + mismatch: { + icon: "fa-times", + level: -1, + }, + } +); + +const passwordIndicator = new InputIndicator( + $(".page.pageLogin .register.side .password.inputAndIndicator"), + { + good: { + icon: "fa-check", + level: 1, + }, + short: { + icon: "fa-times", + level: -1, + }, + weak: { + icon: "fa-times", + level: -1, + }, + } +); + +const verifyPasswordIndicator = new InputIndicator( + $(".page.pageLogin .register.side .verifyPassword.inputAndIndicator"), + { + match: { + icon: "fa-check", + level: 1, + }, + mismatch: { + icon: "fa-times", + level: -1, + }, + } +); + $(".page.pageLogin .register.side .usernameInput").on("input", () => { setTimeout(() => { const val = $( @@ -90,6 +203,57 @@ $(".page.pageLogin .register.side .usernameInput").on("input", () => { }, 1); }); +$(".page.pageLogin .register.side .emailInput").on("input", () => { + if ( + !$(".page.pageLogin .register.side .emailInput").val() && + !$(".page.pageLogin .register.side .verifyEmailInput").val() + ) { + emailIndicator.hide(); + verifyEmailIndicator.hide(); + return; + } + checkEmail(); + checkEmailsMatch(); +}); + +$(".page.pageLogin .register.side .verifyEmailInput").on("input", () => { + if ( + !$(".page.pageLogin .register.side .emailInput").val() && + !$(".page.pageLogin .register.side .verifyEmailInput").val() + ) { + emailIndicator.hide(); + verifyEmailIndicator.hide(); + return; + } + checkEmailsMatch(); +}); + +$(".page.pageLogin .register.side .passwordInput").on("input", () => { + if ( + !$(".page.pageLogin .register.side .passwordInput").val() && + !$(".page.pageLogin .register.side .verifyPasswordInput").val() + ) { + passwordIndicator.hide(); + verifyPasswordIndicator.hide(); + return; + } + checkPassword(); + checkPasswordsMatch(); +}); + +$(".page.pageLogin .register.side .verifyPasswordInput").on("input", () => { + if ( + !$(".page.pageLogin .register.side .passwordInput").val() && + !$(".page.pageLogin .register.side .verifyPasswordInput").val() + ) { + passwordIndicator.hide(); + verifyPasswordIndicator.hide(); + return; + } + checkPassword(); + checkPasswordsMatch(); +}); + export const page = new Page( "login", $(".page.pageLogin"), diff --git a/frontend/static/html/pages/login.html b/frontend/static/html/pages/login.html index 47fa4a262..cd06dc16d 100644 --- a/frontend/static/html/pages/login.html +++ b/frontend/static/html/pages/login.html @@ -13,24 +13,40 @@ autocomplete="new-username" /> - - - - + +
+ +
+
+ +
+
+ +
Sign Up