diff --git a/src/app/auth/page.tsx b/src/app/auth/page.tsx index 547e6b0..cf61087 100644 --- a/src/app/auth/page.tsx +++ b/src/app/auth/page.tsx @@ -20,6 +20,7 @@ import AuthFormWrapper from '@/features/auth/components/AuthFormWrapper'; type Props = object function AuthIndex({ }: Props) { + const [isPending, setIsPending] = useState(false); const [number, setNumber] = useState(""); const [password, setPassword] = useState(""); const [passwordRepeat, setPasswordRepeat] = useState(""); @@ -85,7 +86,7 @@ function AuthIndex({ }: Props) { } } - const onClick = async (e: React.MouseEvent) => { + const onClick = async (e: React.MouseEvent | React.MouseEvent) => { e.stopPropagation(); const target = e.target as HTMLButtonElement; @@ -112,6 +113,7 @@ function AuthIndex({ }: Props) { const onSubmit = async (e: FormEvent) => { e.preventDefault(); + setIsPending(true); if (step == AUTH_STEP.ENTER_NUMBER) { if (true) { @@ -170,18 +172,19 @@ function AuthIndex({ }: Props) { console.log("Could not signup: ", e) } } + setIsPending(false); } const stepMap = { - [AUTH_STEP.ENTER_NUMBER]: , - [AUTH_STEP.ENTER_PASSWORD]: , - [AUTH_STEP.ENTER_OTP]: , - [AUTH_STEP.ENTER_NEW_PASSWORD]: , - [AUTH_STEP.ENTER_RESET_PASSWORD]: , + [AUTH_STEP.ENTER_NUMBER]: , + [AUTH_STEP.ENTER_PASSWORD]: , + [AUTH_STEP.ENTER_OTP]: , + [AUTH_STEP.ENTER_NEW_PASSWORD]: , + [AUTH_STEP.ENTER_RESET_PASSWORD]: , } return ( - + {stepMap[step] || 'say what now?'} ) diff --git a/src/app/globals.css b/src/app/globals.css index 9c3c3d6..64e55d9 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -11,7 +11,8 @@ --color-invalid: red; --color-valid: #439C46; --color-foreground: #333333; - --color-disabled: #FFFFFF61; + --color-disabled: #E7E7E7; + --color-disabled-text: #8C90A3; --radius-normal: 10px; --text-sm2: 13px; --radius-container: 24px; diff --git a/src/components/button/PrimaryButton.tsx b/src/components/button/PrimaryButton.tsx index 44797be..7c872b4 100644 --- a/src/components/button/PrimaryButton.tsx +++ b/src/components/button/PrimaryButton.tsx @@ -2,11 +2,13 @@ import React from 'react' type Props = {} & React.ButtonHTMLAttributes; -function Button({ children, className, ...rest }: Props) { +function Button({ children, className, disabled, ...rest }: Props) { return ( - +
+ +
) } diff --git a/src/components/input/InputField.tsx b/src/components/input/InputField.tsx index 10dcee3..1e62b89 100644 --- a/src/components/input/InputField.tsx +++ b/src/components/input/InputField.tsx @@ -1,4 +1,5 @@ import React from 'react' +import Tooltip from '../utils/Tooltip'; type Props = { htmlFor: string; @@ -17,12 +18,15 @@ function InputField({ onChange, htmlFor, labelText, children, valid = true, clas htmlFor={htmlFor}> {labelText} - - {children} + + ) } diff --git a/src/components/utils/Tooltip.tsx b/src/components/utils/Tooltip.tsx new file mode 100644 index 0000000..d397a91 --- /dev/null +++ b/src/components/utils/Tooltip.tsx @@ -0,0 +1,49 @@ +'use client'; +import React, { ReactNode } from 'react'; + +type Props = { + content: ReactNode; + hidden?: boolean; + position?: 'top' | 'bottom' | 'left' | 'right'; +} & Omit, 'id'>; + +const positionClasses = { + top: 'bottom-full left-1/2 -translate-x-1/2 mb-2', + bottom: 'top-full left-1/2 -translate-x-1/2 mt-2', + left: 'right-full top-1/2 -translate-y-1/2 mr-2', + right: 'left-full top-1/2 -translate-y-1/2 ml-2', +}; + +const arrowClasses = { + top: 'top-full left-1/2 -translate-x-1/2 border-t-disabled', + bottom: 'bottom-full left-1/2 -translate-x-1/2 border-b-disabled', + left: 'left-full top-1/2 -translate-y-1/2 border-l-disabled', + right: 'right-full top-1/2 -translate-y-1/2 border-r-disabled', +}; + +const Tooltip = ({ + children, + content, + hidden = false, + position = 'top', + ...rest +}: Props) => { + + return ( +
+ {children} + {!hidden && ( +
+ {content} +
+
+ )} +
+ ); +}; + +export default Tooltip; diff --git a/src/features/auth/components/AuthFormWrapper.tsx b/src/features/auth/components/AuthFormWrapper.tsx index 89a5095..4c9373a 100644 --- a/src/features/auth/components/AuthFormWrapper.tsx +++ b/src/features/auth/components/AuthFormWrapper.tsx @@ -1,7 +1,7 @@ import React from 'react' type Props = { - + isPending: boolean } & Omit, "id">; function AuthFormWrapper({ children, ...restProps }: Props) { diff --git a/src/features/auth/components/StepEnterNumber.tsx b/src/features/auth/components/StepEnterNumber.tsx index 3c81a6e..87461d4 100644 --- a/src/features/auth/components/StepEnterNumber.tsx +++ b/src/features/auth/components/StepEnterNumber.tsx @@ -2,14 +2,31 @@ import Button from '@/components/button/PrimaryButton'; import InputField from '@/components/input/InputField'; import { AUTH_PAGE_ELEMENT } from '@/enums'; import Image from 'next/image'; -import React from 'react' +import React, { useEffect, useState } from 'react' type Props = { - onChange: ((e: React.ChangeEvent) => void) & React.ChangeEventHandler; + onChange: React.ChangeEventHandler; value: string; -} +} & Omit, 'id'> + +function StepEnterNumber({ onChange, disabled = false, value }: Props) { + const [error, setError] = useState(''); + + const hasError = (e = error) => { + return e.trim().length > 0 + } + + useEffect(() => { + let error = ''; + if (value.length > 0) { + if (!/^09\d{7,9}$/.test(value)) { + error = 'فرمت اشتباه است'; + } + } + + setError(error); + }, [value]) -function StepEnterNumber({ onChange, value }: Props) { return ( <> برای ورود شماره همراه خود را وارد نمایید.

- + ) diff --git a/src/features/auth/components/StepEnterOtp.tsx b/src/features/auth/components/StepEnterOtp.tsx index d461968..a9ac04d 100644 --- a/src/features/auth/components/StepEnterOtp.tsx +++ b/src/features/auth/components/StepEnterOtp.tsx @@ -2,7 +2,7 @@ import Button from '@/components/button/PrimaryButton'; import OTPInputField from '@/components/input/MultiInputField'; import { AUTH_PAGE_ELEMENT, AUTH_PAGE_ELEMENT as AUTH_PAGE_ELEMENTS } from '@/enums'; import Image from 'next/image'; -import React from 'react' +import React, { useEffect, useState } from 'react' type Props = { onChange: ((e: React.ChangeEvent) => void) & React.ChangeEventHandler; @@ -11,9 +11,23 @@ type Props = { phoneNumber: string; timerRunning: boolean; secondsLeft: number; -} +} & Omit, 'id'> -function StepEnterOtp({ onChange, onClick, value, phoneNumber, timerRunning, secondsLeft }: Props) { +function StepEnterOtp({ onChange, onClick, disabled = false, value, phoneNumber, timerRunning, secondsLeft }: Props) { + const [error, setError] = useState(''); + + const hasError = (e = error) => { + return e.trim().length > 0 + } + + useEffect(() => { + let error = ''; + if (!/^\d{6}$/.test(value)) { + error = 'عبارت کامل نیست'; + } + + setError(error); + }, [value]) return ( <> @@ -61,7 +75,7 @@ function StepEnterOtp({ onChange, onClick, value, phoneNumber, timerRunning, sec } - + ) diff --git a/src/features/auth/components/StepEnterPassword.tsx b/src/features/auth/components/StepEnterPassword.tsx index 8b69cb7..ccb8665 100644 --- a/src/features/auth/components/StepEnterPassword.tsx +++ b/src/features/auth/components/StepEnterPassword.tsx @@ -3,7 +3,7 @@ import { EyeToggleIcon } from '@/components/icons/EyeToggleIcon'; import InputField from '@/components/input/InputField'; import { AUTH_PAGE_ELEMENT } from '@/enums'; import Image from 'next/image'; -import React from 'react' +import React, { useEffect, useState } from 'react' type Props = { onChange: ((e: React.ChangeEvent) => void) & React.ChangeEventHandler; @@ -11,9 +11,26 @@ type Props = { value: string; passwordVisible: boolean; rememberMe: boolean; -} +} & Omit, 'id'> + +function StepEnterPassword({ onChange, onClick, disabled = false, value, passwordVisible, rememberMe }: Props) { + const [error, setError] = useState(''); + + const hasError = (e = error) => { + return e.trim().length > 0 + } + + useEffect(() => { + let error = ''; + if (value.length > 0) { + if (!/^.{6,}$/.test(value)) { + error = 'کلمه عبور باید شامل حداقل 6 کاراکتر باشد'; + } + } + + setError(error); + }, [value]) -function StepEnterPassword({ onChange, onClick, value, passwordVisible, rememberMe }: Props) { return ( <> کلمه عبور خود را وارد نمایید.

- + ) diff --git a/src/features/auth/components/StepNewPassword.tsx b/src/features/auth/components/StepNewPassword.tsx index ac420b7..643e60d 100644 --- a/src/features/auth/components/StepNewPassword.tsx +++ b/src/features/auth/components/StepNewPassword.tsx @@ -3,19 +3,51 @@ import { EyeToggleIcon } from '@/components/icons/EyeToggleIcon'; import InputField from '@/components/input/InputField'; import { AUTH_PAGE_ELEMENT } from '@/enums'; import Image from 'next/image'; -import React from 'react' +import React, { useEffect, useState } from 'react' type Props = { onChange: ((e: React.ChangeEvent) => void) & React.ChangeEventHandler; onClick: React.MouseEventHandler | undefined; value: string; repeatValue: string; - reset?: boolean; + isReset?: boolean; passwordVisible: boolean; repeatPasswordVisible: boolean; -} +} & Omit, 'id'> + +function StepNewPassword({ isReset: reset = false, disabled = false, onChange, onClick, value, repeatValue, passwordVisible, repeatPasswordVisible }: Props) { + const [error, setError] = useState(''); + const [error2, setError2] = useState(''); + + const hasError = (e = error) => { + return e.trim().length > 0 + } + + useEffect(() => { + let error = ''; + if (value.length > 0) { + if (!/^.{6,}$/.test(value)) { + error = 'کلمه عبور باید شامل حداقل 6 کاراکتر باشد'; + } + } + + setError(error); + }, [value]) + + useEffect(() => { + let error = ''; + if (value.length > 0) { + if (!/^.{6,}$/.test(value)) { + error = 'کلمه عبور باید شامل حداقل 6 کاراکتر باشد'; + } else if (repeatValue !== value) { + error = 'تکرار کلمه عبور مطابقت ندارد'; + } + } + + setError2(error); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [repeatValue]) -function StepNewPassword({ reset = false, onChange, onClick, value, repeatValue, passwordVisible, repeatPasswordVisible }: Props) { return ( <> کلمه عبور جدید باید ترکیبی از حروف بزرگ و کوچک و نشانه ها باشد مثل A126bdz@

- + )