From 10e1a0f370203f44ad4e063953d3854852f75937 Mon Sep 17 00:00:00 2001 From: Shubham-Lal Date: Wed, 17 Jul 2024 22:48:03 +0530 Subject: [PATCH] client: add regex validation --- client/src/components/card/claim-username.tsx | 2 +- client/src/components/loading/svg.tsx | 6 +- .../src/components/modal/auth/brief-info.tsx | 4 +- .../components/modal/auth/forgot-password.tsx | 70 ++++++++++++------- .../src/components/modal/auth/signup-tab.tsx | 12 +++- client/src/components/sidebar/explore.css | 1 - client/src/data/regex.ts | 3 + client/src/pages/create-debate/style.css | 4 +- 8 files changed, 63 insertions(+), 39 deletions(-) create mode 100644 client/src/data/regex.ts diff --git a/client/src/components/card/claim-username.tsx b/client/src/components/card/claim-username.tsx index 4c1c4d2..366e540 100644 --- a/client/src/components/card/claim-username.tsx +++ b/client/src/components/card/claim-username.tsx @@ -5,6 +5,7 @@ import { toast } from "sonner" import { PiArrowUpRightBold } from "react-icons/pi" import { LoadingSVG } from "../loading/svg" import { useNavStore } from "../../store/useNavStore" +import { usernameRegex } from "../../data/regex" const ClaimUsername = () => { const { setAuthTab } = useAuthStore(); @@ -23,7 +24,6 @@ const ClaimUsername = () => { const inputUsername = e.target.value; setUsername(inputUsername); - const usernameRegex = /^[a-zA-Z0-9_-]+$/; if (inputUsername) { if (!usernameRegex.test(inputUsername)) { setMessage({ type: 'error', content: 'Username can only contain alphanumeric characters, underscores (_) and hyphens (-). No spaces or other special characters are allowed.' }); diff --git a/client/src/components/loading/svg.tsx b/client/src/components/loading/svg.tsx index 398e10f..8012a33 100644 --- a/client/src/components/loading/svg.tsx +++ b/client/src/components/loading/svg.tsx @@ -2,7 +2,7 @@ import "./style.css" interface Props { size: number - color ?: string + color?: string } const LoadingSVG: React.FC = ({ size, color }) => { @@ -17,9 +17,7 @@ const LoadingSVG: React.FC = ({ size, color }) => { const LoadingComponent = () => { return (
- +
) } diff --git a/client/src/components/modal/auth/brief-info.tsx b/client/src/components/modal/auth/brief-info.tsx index df581b4..4544f64 100644 --- a/client/src/components/modal/auth/brief-info.tsx +++ b/client/src/components/modal/auth/brief-info.tsx @@ -7,6 +7,7 @@ import { AuthStatus, AuthTab, useAuthStore, useTempStore } from "../../../store/ import { MdModeEdit } from "react-icons/md" import { GrCloudUpload } from "react-icons/gr" import { IoPersonCircleOutline } from "react-icons/io5" +import { specialCharRegex, usernameRegex } from "../../../data/regex" const BriefInfo: React.FC = ({ registerData, setRegisterData }) => { const navigate = useNavigate(); @@ -24,9 +25,6 @@ const BriefInfo: React.FC = ({ registerData, setRegisterData const handleInputChange = useCallback((e: React.ChangeEvent) => { const { name, value } = e.target; - // eslint-disable-next-line no-useless-escape - const specialCharRegex = /[@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?]/; - const usernameRegex = /^[a-zA-Z0-9_-]+$/; let isValid = true; if (name === 'username') { diff --git a/client/src/components/modal/auth/forgot-password.tsx b/client/src/components/modal/auth/forgot-password.tsx index f2edbea..af33e14 100644 --- a/client/src/components/modal/auth/forgot-password.tsx +++ b/client/src/components/modal/auth/forgot-password.tsx @@ -1,5 +1,8 @@ -import { useState, useCallback } from "react"; -import { useAuthStore, AuthTab } from "../../../store/useAuthStore"; +import { useState, useCallback } from "react" +import { useAuthStore, AuthTab } from "../../../store/useAuthStore" +import { toast } from "sonner" +import { LoadingSVG } from "../../loading/svg"; +import { emailRegex } from "../../../data/regex" const ForgotPassword = () => { const { setAuthTab } = useAuthStore(); @@ -9,25 +12,20 @@ const ForgotPassword = () => { username: "" }); const [isSubmitted, setIsSubmitted] = useState(false); - const [validationState, setValidationState] = useState({ - isEmailValid: true, - isUsernameValid: true - }); + const [validationState, setValidationState] = useState(true); const handleInputChange = useCallback((e: React.ChangeEvent) => { const { name, value } = e.target; - setForgotData(prevState => ({ - ...prevState, - [name]: value - })); - setValidationState(prevState => ({ - ...prevState, - [`is${name.charAt(0).toUpperCase() + name.slice(1)}Valid`]: !!value + setForgotData(() => ({ + email: name === "email" ? value : "", + username: name === "username" ? value : "" })); + + setValidationState(!!value); }, []); - const handleForgotSubmit = (e: React.FormEvent) => { + const handleForgotSubmit = async (e: React.FormEvent) => { e.preventDefault(); setIsSubmitted(true); @@ -40,14 +38,30 @@ const ForgotPassword = () => { username: trimmedUsername })); - setValidationState({ - isEmailValid: !!trimmedEmail, - isUsernameValid: !!trimmedUsername - }); + setValidationState(!!trimmedEmail || !!trimmedUsername); - if (trimmedEmail || trimmedUsername) { - // Perform further actions for forgot password + if (!emailRegex.test(trimmedEmail)) { + setIsSubmitted(false); + return toast.warning('Invalid email address'); } + + if (trimmedEmail || trimmedUsername) { + await fetch(`${import.meta.env.VITE_SERVER_URL}/api/auth/recover-account`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(trimmedEmail ? { email: trimmedEmail } : { username: trimmedUsername }) + }) + .then(res => res.json()) + .then(response => { + if (response.success) toast.success(response.message); + else { + if (response.message === 'Validation failed') { + return toast.error(`${response.errors[0].message.charAt(0).toUpperCase() + response.errors[0].message.slice(1)}`) + } + toast.error(response.message) + }; + }).finally(() => setIsSubmitted(false)); + } else setTimeout(() => setIsSubmitted(false), 500); }; return ( @@ -60,8 +74,8 @@ const ForgotPassword = () => { name="email" value={forgotData.email} onChange={handleInputChange} - className={`${isSubmitted && !validationState.isEmailValid ? "shake" : ""}`} - style={{ borderColor: isSubmitted && !validationState.isEmailValid ? "red" : "" }} + className={`${isSubmitted && !validationState ? "shake" : ""}`} + style={{ borderColor: isSubmitted && !validationState ? "red" : "" }} placeholder='Enter your email' /> @@ -76,13 +90,17 @@ const ForgotPassword = () => { name="username" value={forgotData.username} onChange={handleInputChange} - className={`${isSubmitted && !validationState.isUsernameValid ? "shake" : ""}`} - style={{ borderColor: isSubmitted && !validationState.isUsernameValid ? "red" : "" }} + className={`${isSubmitted && !validationState ? "shake" : ""}`} + style={{ borderColor: isSubmitted && !validationState ? "red" : "" }} placeholder='Enter your username' /> -

setAuthTab(AuthTab.Login)}>Go Back

diff --git a/client/src/components/modal/auth/signup-tab.tsx b/client/src/components/modal/auth/signup-tab.tsx index 1ffa8c2..a195605 100644 --- a/client/src/components/modal/auth/signup-tab.tsx +++ b/client/src/components/modal/auth/signup-tab.tsx @@ -4,6 +4,7 @@ import { toast } from "sonner" import { AuthStatus, AuthTab, useAuthStore, useTempStore } from "../../../store/useAuthStore" import { FcGoogle } from "react-icons/fc" import { LoadingSVG } from "../../loading/svg" +import { emailRegex } from "../../../data/regex" const SignupTab: React.FC = ({ registerData, setRegisterData }) => { const { setAuthTab, isAuthenticated } = useAuthStore(); @@ -51,8 +52,11 @@ const SignupTab: React.FC = ({ registerData, setRegisterData }); if (trimmedEmail && trimmedPassword) { + if (!emailRegex.test(trimmedEmail)) { + return toast.warning('Invalid email address'); + } if (trimmedPassword.length < 6) { - return toast.warning('Password should be atleast 6 digits') + return toast.warning('Password should be atleast 6 digits'); } setAuthTab(AuthTab.Info); } @@ -109,7 +113,11 @@ const SignupTab: React.FC = ({ registerData, setRegisterData placeholder={isSubmitted && !validationState.isPasswordValid ? 'Required' : ''} />
-
diff --git a/client/src/components/sidebar/explore.css b/client/src/components/sidebar/explore.css index f425c2e..a1c911c 100644 --- a/client/src/components/sidebar/explore.css +++ b/client/src/components/sidebar/explore.css @@ -60,7 +60,6 @@ #explore .explore-btns a { padding: 7.5px 15px; - background-color: var(--body_background); border-radius: 20px; text-transform: lowercase; } diff --git a/client/src/data/regex.ts b/client/src/data/regex.ts new file mode 100644 index 0000000..e4b4361 --- /dev/null +++ b/client/src/data/regex.ts @@ -0,0 +1,3 @@ +export const usernameRegex = /^[a-zA-Z0-9_-]+$/; +export const specialCharRegex = /[@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?]/; +export 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]+\.)+(com|org|net|gov|mil|biz|info|mobi|name|aero|jobs|museum|in|space)))$/; \ No newline at end of file diff --git a/client/src/pages/create-debate/style.css b/client/src/pages/create-debate/style.css index 9c6ca06..23afb15 100644 --- a/client/src/pages/create-debate/style.css +++ b/client/src/pages/create-debate/style.css @@ -17,7 +17,7 @@ #create { position: relative; overflow: hidden; - overscroll-behavior: none; + overscroll-behavior: contain; } #create-debate { @@ -130,7 +130,7 @@ .preview .e-richtexteditor { min-height: fit-content !important; height: fit-content !important; - background-color: var(--body_background); + background-color: transparent; border: none; }