diff --git a/src/app/auth/page.tsx b/src/app/auth/page.tsx index 1cbfc9f..9eb4e27 100644 --- a/src/app/auth/page.tsx +++ b/src/app/auth/page.tsx @@ -9,17 +9,22 @@ import StepEnterNumber from '@/features/auth/components/StepEnterNumber'; import StepEnterOtp from '@/features/auth/components/StepEnterOtp'; import { AUTH_PAGE_ELEMENT, AUTH_STEP } from '@/enums'; import { useCountdown } from '@/hooks/useCountdown'; +import StepNewPassword from '@/features/auth/components/StepNewPassword'; +import { validatePhoneNumber } from '@/features/auth/actions/validatePhoneNumber'; +import { signupUser } from '@/features/auth/actions/signupUser'; type Props = object function AuthIndex({ }: Props) { const [number, setNumber] = useState(""); const [password, setPassword] = useState(""); + const [passwordRepeat, setPasswordRepeat] = useState(""); const [otp, setOtp] = useState(""); const [showPassword, setShowPassword] = useState(false); + const [showPasswordRepeat, setShowPasswordRepeat] = useState(false); const [rememberMe, setRememberMe] = useState(false); const [step, setStep] = useState(AUTH_STEP.ENTER_NUMBER); - const { timerRunning, secondsLeft, restart } = useCountdown(step === AUTH_STEP.ENTER_OTP); + const { timerRunning, secondsLeft, restart, stop } = useCountdown(step === AUTH_STEP.ENTER_OTP); const isAuthenticated = useAuthStore((state) => state.isAuthenticated) @@ -29,17 +34,20 @@ function AuthIndex({ }: Props) { } }, [isAuthenticated]) - const updateInput = (e: ChangeEvent) => { - if (e.target.id == "phone") { + const onChange = (e: ChangeEvent) => { + if (e.target.id == AUTH_PAGE_ELEMENT.INPUT_PHONE) { setNumber(() => e.target.value) } - else if (e.target.id == "password") { + else if (e.target.id == AUTH_PAGE_ELEMENT.INPUT_PASSWORD) { setPassword(() => e.target.value) } - else if (e.target.id == "rememberMe") { + else if (e.target.id == AUTH_PAGE_ELEMENT.INPUT_PASSWORD_REPEAT) { + setPasswordRepeat(() => e.target.value) + } + else if (e.target.id == AUTH_PAGE_ELEMENT.INPUT_REMEMBER_ME) { setRememberMe((state) => !state) } - else if (e.target.id.startsWith("otp-data")) { + else if (e.target.id.startsWith(AUTH_PAGE_ELEMENT.INPUT_OTP)) { const index = +e.target.id.split('-')[2]; const prev = otp.padEnd(6).split(''); prev[index] = e.target.value; @@ -48,15 +56,18 @@ function AuthIndex({ }: Props) { } } - const onClickEvent = (e: React.MouseEvent) => { + const onClick = (e: React.MouseEvent) => { e.stopPropagation(); const target = e.target as HTMLButtonElement; if (target.id === AUTH_PAGE_ELEMENT.TOGGLE_PASSWORD) { setShowPassword((state) => !state); } + if (target.id === AUTH_PAGE_ELEMENT.TOGGLE_REPEAT_PASSWORD) { + setShowPasswordRepeat((state) => !state); + } else if (target.id === AUTH_PAGE_ELEMENT.RESET_PASSWORD) { - setShowPassword((state) => !state); + setStep(() => AUTH_STEP.ENTER_OTP) } else if (target.id === AUTH_PAGE_ELEMENT.RESEND_OTP) { if(!timerRunning) { @@ -65,12 +76,16 @@ function AuthIndex({ }: Props) { } }; - const submit = async (e: FormEvent) => { + const onSubmit = async (e: FormEvent) => { e.preventDefault(); if (step == AUTH_STEP.ENTER_NUMBER) { - setStep(AUTH_STEP.ENTER_PASSWORD) + if(await validatePhoneNumber(number)) { + setStep(AUTH_STEP.ENTER_PASSWORD) + } else { + setStep(AUTH_STEP.ENTER_OTP); + } } - if (step == AUTH_STEP.ENTER_PASSWORD) { + else if (step == AUTH_STEP.ENTER_PASSWORD) { if (await loginUser(number, password)) { console.log("Logged in") } @@ -78,17 +93,34 @@ function AuthIndex({ }: Props) { console.log("Wrong credentials") } } - if (step == AUTH_STEP.ENTER_OTP) { + else if (step == AUTH_STEP.ENTER_OTP) { + stop(); + if(await validatePhoneNumber(number)) { + setStep(AUTH_STEP.ENTER_RESET_PASSWORD) + } else { + setStep(AUTH_STEP.ENTER_NEW_PASSWORD) + } + } + else if (step == AUTH_STEP.ENTER_RESET_PASSWORD) { + console.log("Password changed") setStep(AUTH_STEP.ENTER_NUMBER) } + else if (step == AUTH_STEP.ENTER_NEW_PASSWORD) { + if(await signupUser(number, password)) { + console.log("Signed up") + redirect("/") + } else { + console.log("Could not signup") + } + } } if (step == AUTH_STEP.ENTER_NUMBER) { // First Step -> Enter number return ( ) @@ -97,9 +129,9 @@ function AuthIndex({ }: Props) { if (step == AUTH_STEP.ENTER_PASSWORD) { return ( ) } + + if (step == AUTH_STEP.ENTER_NEW_PASSWORD) { + return ( + + ) + } + + if (step == AUTH_STEP.ENTER_RESET_PASSWORD) { + return ( + + ) + } } export default AuthIndex diff --git a/src/components/button/PrimaryButton.tsx b/src/components/button/PrimaryButton.tsx index 6f9a304..1360a32 100644 --- a/src/components/button/PrimaryButton.tsx +++ b/src/components/button/PrimaryButton.tsx @@ -1,11 +1,11 @@ import React from 'react' -type Props = { } & React.ButtonHTMLAttributes; +type Props = {} & React.ButtonHTMLAttributes; -function PrimaryButton({children, ...rest}: Props) { +function PrimaryButton({ children, className, ...rest }: Props) { return ( - ) } diff --git a/src/components/input/InputField.tsx b/src/components/input/InputField.tsx index 336ad1d..2fd2950 100644 --- a/src/components/input/InputField.tsx +++ b/src/components/input/InputField.tsx @@ -5,12 +5,13 @@ type Props = { labelText?: React.ReactNode; value?: string; onChange: (e: React.ChangeEvent) => void; + valid?: boolean } & Omit, "id">; -function InputField({ onChange, htmlFor, labelText, children, className, ...inputProps }: Props) { +function InputField({ onChange, htmlFor, labelText, children, valid = true, className, ...inputProps }: Props) { return ( -
+
{/* TODO: customize checkbox */} - -
+ + ورود به منو diff --git a/src/features/auth/components/StepNewPassword.tsx b/src/features/auth/components/StepNewPassword.tsx new file mode 100644 index 0000000..0c6b911 --- /dev/null +++ b/src/features/auth/components/StepNewPassword.tsx @@ -0,0 +1,60 @@ +import PrimaryButton from '@/components/button/PrimaryButton'; +import { EyeToggleIcon } from '@/components/icons/EyeToggleIcon'; +import InputField from '@/components/input/InputField'; +import { AUTH_PAGE_ELEMENT } from '@/enums'; +import React from 'react' + +type Props = { + onSubmit: React.FormEventHandler | undefined; + onChange: ((e: React.ChangeEvent) => void) & React.ChangeEventHandler; + onClick: React.MouseEventHandler | undefined; + value: string; + repeatValue: string; + reset?: boolean; + passwordVisible: boolean; + repeatPasswordVisible: boolean; +} + +function StepNewPassword({ reset = false, onSubmit, onChange, onClick, value, repeatValue, passwordVisible, repeatPasswordVisible }: Props) { + return ( +
+
+ +
+
+ { reset ? "تغییر کلمه عبور" : "انتخاب کلمه عبور" } +
+

کلمه عبور جدید باید ترکیبی از حروف بزرگ و کوچک و نشانه ها باشد مثل A126bdz@

+
+ + + + + + +
+ ورود +
+ ) +} + +export default StepNewPassword \ No newline at end of file diff --git a/src/lib/api/auth.ts b/src/lib/api/auth.ts new file mode 100644 index 0000000..8db1732 --- /dev/null +++ b/src/lib/api/auth.ts @@ -0,0 +1,41 @@ +const BASE_URL = process.env.NEXT_PUBLIC_API_URL || 'https://api.example.com' + +export async function checkUserExists(phoneNumber: string): Promise { + const res = phoneNumber === "123"; + const data = { status: 200, result: { exists: res } } + return data.result.exists +} + +export async function signup(userData: { + phoneNumber: string, + password: string +}) { + const res = userData.phoneNumber === "123" && userData.password === "123"; + const data = { sucess: res } + return { + data, + user: { + id: '123', + name: 'John Doe', + number: userData.phoneNumber, + token: 'mock-jwt-token', + } + } +} + +export async function login(credentials: { + phoneNumber: string, + password: string +}) { + const res = credentials.phoneNumber === "1234" && credentials.password === "1234"; + const data = { sucess: res } + return { + data, + user: { + id: '1234', + name: 'Foo bar', + number: credentials.phoneNumber, + token: 'mock-jwt-token', + } + } +}