From 6d5ce71b2f28cc2287971ec35ff945f275118dfc Mon Sep 17 00:00:00 2001 From: hamid zarghami Date: Tue, 24 Dec 2024 11:43:25 +0330 Subject: [PATCH] login --- package-lock.json | 176 ++++++++++++++++++++++- package.json | 6 +- src/App.tsx | 8 +- src/assets/images/arrow-left.svg | 4 + src/assets/images/eye.svg | 4 + src/components/Error.tsx | 15 ++ src/components/Input.tsx | 15 +- src/config/Pages.ts | 1 + src/hooks/useCountDown.ts | 71 +++++++++ src/index.css | 6 + src/langs/fa.json | 16 ++- src/pages/auth/Login.tsx | 55 +++---- src/pages/auth/components/LoginStep1.tsx | 82 +++++++++++ src/pages/auth/components/LoginStep2.tsx | 91 ++++++++++++ src/pages/auth/components/LoginStep3.tsx | 84 +++++++++++ src/pages/auth/types/AuthTypes.ts | 13 ++ src/pages/store/AuthStore.ts | 13 ++ 17 files changed, 609 insertions(+), 51 deletions(-) create mode 100644 src/assets/images/arrow-left.svg create mode 100644 src/assets/images/eye.svg create mode 100644 src/components/Error.tsx create mode 100755 src/hooks/useCountDown.ts create mode 100644 src/pages/auth/components/LoginStep1.tsx create mode 100644 src/pages/auth/components/LoginStep2.tsx create mode 100644 src/pages/auth/components/LoginStep3.tsx create mode 100644 src/pages/auth/types/AuthTypes.ts create mode 100644 src/pages/store/AuthStore.ts diff --git a/package-lock.json b/package-lock.json index 68ff385..5ecbbfd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,15 +9,19 @@ "version": "0.0.0", "dependencies": { "@tanstack/react-query": "^5.62.8", + "formik": "^2.4.6", "i18next": "^24.2.0", "react": "^19.0.0", "react-dom": "^19.0.0", "react-i18next": "^15.2.0", "react-loading": "^2.0.3", "react-multi-date-picker": "^4.5.2", + "react-otp-input": "^3.1.1", "react-router-dom": "^7.1.0", "react-toastify": "^11.0.2", - "tailwind-merge": "^2.5.5" + "tailwind-merge": "^2.5.5", + "yup": "^1.6.1", + "zustand": "^5.0.2" }, "devDependencies": { "@eslint/js": "^9.17.0", @@ -1441,6 +1445,16 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/hoist-non-react-statics": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.6.tgz", + "integrity": "sha512-lPByRJUer/iN/xa4qpyL0qmL11DqNW81iU/IG1S3uvRUq4oKagz8VCxZjiWkumgt66YT3vOdDgZ0o32sGKtCEw==", + "license": "MIT", + "dependencies": { + "@types/react": "*", + "hoist-non-react-statics": "^3.3.0" + } + }, "node_modules/@types/json-schema": { "version": "7.0.15", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", @@ -1452,14 +1466,12 @@ "version": "15.7.14", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.14.tgz", "integrity": "sha512-gNMvNH49DJ7OJYv+KAKn0Xp45p8PLl6zo2YnvDIbTd4J6MER2BmWN49TG7n9LvkyihINxeKW8+3bfS2yDC9dzQ==", - "dev": true, "license": "MIT" }, "node_modules/@types/react": { "version": "18.3.18", "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.18.tgz", "integrity": "sha512-t4yC+vtgnkYjNSKlFx1jkAhH8LgTo2N/7Qvi83kdEaUtMDiwpbLAktKDaAMlRcJ5eSxZkH74eEGt1ky31d7kfQ==", - "dev": true, "license": "MIT", "dependencies": { "@types/prop-types": "*", @@ -2124,7 +2136,6 @@ "version": "3.1.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", - "dev": true, "license": "MIT" }, "node_modules/debug": { @@ -2152,6 +2163,15 @@ "dev": true, "license": "MIT" }, + "node_modules/deepmerge": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-2.2.1.tgz", + "integrity": "sha512-R9hc1Xa/NOBi9WRVUWg19rl1UB7Tt4kuPd+thNJgFZoxXsTz7ncaPaeIm+40oSGuP33DfMb4sZt1QIGiJzC4EA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/didyoumean": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", @@ -2569,6 +2589,31 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/formik": { + "version": "2.4.6", + "resolved": "https://registry.npmjs.org/formik/-/formik-2.4.6.tgz", + "integrity": "sha512-A+2EI7U7aG296q2TLGvNapDNTZp1khVt5Vk0Q/fyfSROss0V/V6+txt2aJnwEos44IxTCW/LYAi/zgWzlevj+g==", + "funding": [ + { + "type": "individual", + "url": "https://opencollective.com/formik" + } + ], + "license": "Apache-2.0", + "dependencies": { + "@types/hoist-non-react-statics": "^3.3.1", + "deepmerge": "^2.1.1", + "hoist-non-react-statics": "^3.3.0", + "lodash": "^4.17.21", + "lodash-es": "^4.17.21", + "react-fast-compare": "^2.0.1", + "tiny-warning": "^1.0.2", + "tslib": "^2.0.0" + }, + "peerDependencies": { + "react": ">=16.8.0" + } + }, "node_modules/fraction.js": { "version": "4.3.7", "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", @@ -2721,6 +2766,15 @@ "node": ">= 0.4" } }, + "node_modules/hoist-non-react-statics": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", + "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", + "license": "BSD-3-Clause", + "dependencies": { + "react-is": "^16.7.0" + } + }, "node_modules/html-parse-stringify": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/html-parse-stringify/-/html-parse-stringify-3.0.1.tgz", @@ -3029,6 +3083,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "license": "MIT" + }, + "node_modules/lodash-es": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", + "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==", + "license": "MIT" + }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", @@ -3530,6 +3596,12 @@ "react-is": "^16.13.1" } }, + "node_modules/property-expr": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/property-expr/-/property-expr-2.0.6.tgz", + "integrity": "sha512-SVtmxhRE/CGkn3eZY1T6pC8Nln6Fr/lu1mKSgRud0eC73whjGfoAogbn78LkD8aFL0zz3bAFerKSnOl7NlErBA==", + "license": "MIT" + }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -3598,6 +3670,12 @@ "react-dom": ">=16.8.0" } }, + "node_modules/react-fast-compare": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-2.0.4.tgz", + "integrity": "sha512-suNP+J1VU1MWFKcyt7RtjiSWUjvidmQSlqu+eHslq+342xCbGTYmC0mEhPCOHxlW0CywylOC1u2DFAT+bv4dBw==", + "license": "MIT" + }, "node_modules/react-i18next": { "version": "15.2.0", "resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-15.2.0.tgz", @@ -3624,8 +3702,7 @@ "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/react-loading": { "version": "2.0.3", @@ -3651,6 +3728,16 @@ "react-dom": ">=16.8.0" } }, + "node_modules/react-otp-input": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/react-otp-input/-/react-otp-input-3.1.1.tgz", + "integrity": "sha512-bjPavgJ0/Zmf/AYi4onj8FbH93IjeD+e8pWwxIJreDEWsU1ILR5fs8jEJmMGWSBe/yyvPP6X/W6Mk9UkOCkTPw==", + "license": "MIT", + "peerDependencies": { + "react": ">=16.8.6 || ^17.0.0 || ^18.0.0", + "react-dom": ">=16.8.6 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/react-refresh": { "version": "0.14.2", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.2.tgz", @@ -4153,6 +4240,18 @@ "node": ">=0.8" } }, + "node_modules/tiny-case": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tiny-case/-/tiny-case-1.0.3.tgz", + "integrity": "sha512-Eet/eeMhkO6TX8mnUteS9zgPbUMQa4I6Kkp5ORiBD5476/m+PIRiumP5tmh5ioJpH7k51Kehawy2UDfsnxxY8Q==", + "license": "MIT" + }, + "node_modules/tiny-warning": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz", + "integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==", + "license": "MIT" + }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -4166,6 +4265,12 @@ "node": ">=8.0" } }, + "node_modules/toposort": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/toposort/-/toposort-2.0.2.tgz", + "integrity": "sha512-0a5EOkAUp8D4moMi2W8ZF8jcga7BgZd91O/yabJCFY8az+XSzeGyTKs0Aoo897iV1Nj6guFq8orWDS96z91oGg==", + "license": "MIT" + }, "node_modules/ts-api-utils": { "version": "1.4.3", "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.4.3.tgz", @@ -4186,6 +4291,12 @@ "dev": true, "license": "Apache-2.0" }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, "node_modules/turbo-stream": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/turbo-stream/-/turbo-stream-2.4.0.tgz", @@ -4205,6 +4316,18 @@ "node": ">= 0.8.0" } }, + "node_modules/type-fest": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", + "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==", + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/typescript": { "version": "5.6.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz", @@ -4524,6 +4647,47 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } + }, + "node_modules/yup": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/yup/-/yup-1.6.1.tgz", + "integrity": "sha512-JED8pB50qbA4FOkDol0bYF/p60qSEDQqBD0/qeIrUCG1KbPBIQ776fCUNb9ldbPcSTxA69g/47XTo4TqWiuXOA==", + "license": "MIT", + "dependencies": { + "property-expr": "^2.0.5", + "tiny-case": "^1.0.3", + "toposort": "^2.0.2", + "type-fest": "^2.19.0" + } + }, + "node_modules/zustand": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/zustand/-/zustand-5.0.2.tgz", + "integrity": "sha512-8qNdnJVJlHlrKXi50LDqqUNmUbuBjoKLrYQBnoChIbVph7vni+sY+YpvdjXG9YLd/Bxr6scMcR+rm5H3aSqPaw==", + "license": "MIT", + "engines": { + "node": ">=12.20.0" + }, + "peerDependencies": { + "@types/react": ">=18.0.0", + "immer": ">=9.0.6", + "react": ">=18.0.0", + "use-sync-external-store": ">=1.2.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "immer": { + "optional": true + }, + "react": { + "optional": true + }, + "use-sync-external-store": { + "optional": true + } + } } } } diff --git a/package.json b/package.json index 3d5c5a3..0fcb0ec 100644 --- a/package.json +++ b/package.json @@ -11,15 +11,19 @@ }, "dependencies": { "@tanstack/react-query": "^5.62.8", + "formik": "^2.4.6", "i18next": "^24.2.0", "react": "^19.0.0", "react-dom": "^19.0.0", "react-i18next": "^15.2.0", "react-loading": "^2.0.3", "react-multi-date-picker": "^4.5.2", + "react-otp-input": "^3.1.1", "react-router-dom": "^7.1.0", "react-toastify": "^11.0.2", - "tailwind-merge": "^2.5.5" + "tailwind-merge": "^2.5.5", + "yup": "^1.6.1", + "zustand": "^5.0.2" }, "devDependencies": { "@eslint/js": "^9.17.0", diff --git a/src/App.tsx b/src/App.tsx index e7f6757..0d00056 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -39,18 +39,22 @@ const queryClient = new QueryClient({ const App: FC = () => { const [isLogin, setIsLogin] = useState<'checking' | 'isLogin' | 'isNotLogin'>('checking') - useEffect(() => { if (localStorage.getItem(import.meta.env.VITE_TOKEN_NAME)) { setIsLogin('isLogin') } else { setIsLogin('isNotLogin') - window.location.href = Pages.auth.login + if (window.location.href.split('auth').length === 1) { + window.location.href = Pages.auth.login + } } }, []) + console.log('us', isLogin); + + return ( diff --git a/src/assets/images/arrow-left.svg b/src/assets/images/arrow-left.svg new file mode 100644 index 0000000..b848f88 --- /dev/null +++ b/src/assets/images/arrow-left.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/assets/images/eye.svg b/src/assets/images/eye.svg new file mode 100644 index 0000000..de3cfe0 --- /dev/null +++ b/src/assets/images/eye.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/components/Error.tsx b/src/components/Error.tsx new file mode 100644 index 0000000..7fec2e7 --- /dev/null +++ b/src/components/Error.tsx @@ -0,0 +1,15 @@ +import { FC } from 'react' + +type Props = { + errorText: string +} + +const Error: FC = (props: Props) => { + return ( +
+ {props.errorText} +
+ ) +} + +export default Error \ No newline at end of file diff --git a/src/components/Input.tsx b/src/components/Input.tsx index 1ba013d..6b81c62 100644 --- a/src/components/Input.tsx +++ b/src/components/Input.tsx @@ -1,5 +1,6 @@ -import { FC, InputHTMLAttributes } from 'react' +import { FC, InputHTMLAttributes, useState } from 'react' import { clx } from '../helpers/utils'; +import EyeIcon from '../assets/images/eye.svg' type Variant = "floating_outlined" | "primary" | "search"; @@ -16,19 +17,29 @@ type Props = { const Input: FC = (props: Props) => { + const [showPassword, setShowPassword] = useState(false) + const inputClass = clx( 'w-full h-10 text-black block px-4 text-xs rounded-xl mt-1 border border-border', props.readOnly && 'bg-gray-100', props.className ); + return (
- +
+ + + { + props.type === 'password' && + setShowPassword((oldValue) => !oldValue)} src={EyeIcon} className='w-5 absolute top-0 bottom-0 cursor-pointer my-auto left-3' /> + } +
) } diff --git a/src/config/Pages.ts b/src/config/Pages.ts index 0eef1df..ba89ecc 100644 --- a/src/config/Pages.ts +++ b/src/config/Pages.ts @@ -2,5 +2,6 @@ export const Pages = { auth: { login: "/auth/login", register: "/auth/register", + forgotPassword: "/auth/forgot", }, }; diff --git a/src/hooks/useCountDown.ts b/src/hooks/useCountDown.ts new file mode 100755 index 0000000..944028b --- /dev/null +++ b/src/hooks/useCountDown.ts @@ -0,0 +1,71 @@ +import { useEffect, useState } from 'react'; + +export interface ICountDown { + m: number; + s: number; +} +/** + * + * @param minutes how many minutes. + * @param autoplay by default true. if you want play coundDown after an action, you can set it to false and manually play it. + * @param onEnd you can pass a callback function that will trigger at the END. + * @returns + */ + +export function useCountDown(minutes: number, autoplay = true, onEnd = () => {}) { + + const [countDown, setCountDown] = useState({m: minutes, s: 0}); + const [stop, setStop] = useState(!autoplay); + + useEffect(() => { + const handler = setTimeout(() => { + if (countDown.m !== 0 || countDown.s !== 0) { + setCountDown(calculateCountDown(countDown)); + } else { + onEnd(); + } + }, 1000); + + return () => { + clearTimeout(handler); + }; + }, [countDown]); + + const reset = () => { + setCountDown({m: minutes, s: 0}); + }; + const pause = () => { + setStop(true); + }; + const play = () => { + setStop(false); + setCountDown({...countDown}); + }; + + const calculateCountDown = (st: ICountDown): ICountDown => { + if (stop) { + return countDown; + } + const timeLeft = { m: 0, s: 0 }; + const remain = st.m * 60 + st.s - 1; + if (remain > 0) { + timeLeft.m = Math.floor((remain) / 60); + timeLeft.s = remain - timeLeft.m * 60; + return timeLeft; + } else { + if (remain === 0 && countDown.s === 1) { + return timeLeft; + } else { + return countDown; + } + } + }; + + + return { + value: `${countDown.m > 9 ? countDown.m : '0' + countDown.m}:${countDown.s > 9 ? countDown.s : '0' + countDown.s}`, + reset, + pause, + play + }; +} \ No newline at end of file diff --git a/src/index.css b/src/index.css index c3a3481..fb14904 100644 --- a/src/index.css +++ b/src/index.css @@ -47,3 +47,9 @@ textarea::placeholder { .rmdp-container { width: 100%; } +.dltr { + direction: ltr; +} +.otp div { + width: 100%; +} diff --git a/src/langs/fa.json b/src/langs/fa.json index 22d2270..e6a7379 100644 --- a/src/langs/fa.json +++ b/src/langs/fa.json @@ -24,6 +24,20 @@ "mobile_or_email": " شماره موبایل یا ایمیل", "enter_mobile_or_email": " شماره موبایل یا ایمیل خود را وارد کنید", "have_account": "آیا حساب کاربری ندارید؟", - "register": "ثبت نام" + "register": "ثبت نام", + "enter_otp": "کد تایید را وارد کنید", + "verfify_code_text_1": "کد تایید برای ", + "verfify_code_text_2": "ارسال شد.", + "change_number": "تغییر شماره", + "verify_code": "کد تایید ", + "counter_otp": "مانده تا دریافت مجدد کد", + "login_with_password": "ورود با رمز عبور", + "enter_password_step3": "رمز عبور را وارد کنید", + "enter_your_password": "رمز عبور خود را وارد کنید.", + "forgot_password": "فراموشی رمز عبور", + "login_with_otp": "ورود با رمز عبور یکبار مصرف" + }, + "errors": { + "required": "این فیلد اجباری می باشد" } } diff --git a/src/pages/auth/Login.tsx b/src/pages/auth/Login.tsx index f812446..8265e20 100644 --- a/src/pages/auth/Login.tsx +++ b/src/pages/auth/Login.tsx @@ -1,15 +1,15 @@ import { FC } from 'react' import LogoImage from '../../assets/images/logo.svg' import LogoSmallImage from '../../assets/images/logo-small.svg' -import { useTranslation } from 'react-i18next' -import Input from '../../components/Input' -import Button from '../../components/Button' -import { Link } from 'react-router-dom' -import { Pages } from '../../config/Pages' +import { useAuthStore } from '../store/AuthStore' +import LoginStep1 from './components/LoginStep1' +import LoginStep2 from './components/LoginStep2' +import LoginStep3 from './components/LoginStep3' const Login: FC = () => { - const { t } = useTranslation('global') + const { stepLogin } = useAuthStore() + return (
@@ -19,39 +19,16 @@ const Login: FC = () => {
-
-

- {t('auth.welcome')} -

-

- {t('auth.enter_info_login')} -

-
- -
- - -
- - -
diff --git a/src/pages/auth/components/LoginStep1.tsx b/src/pages/auth/components/LoginStep1.tsx new file mode 100644 index 0000000..14fbb88 --- /dev/null +++ b/src/pages/auth/components/LoginStep1.tsx @@ -0,0 +1,82 @@ +import { FC } from 'react' +import Input from '../../../components/Input' +import { useTranslation } from 'react-i18next' +import { LoginType } from '../types/AuthTypes' +import { useFormik } from 'formik' +import * as Yup from 'yup' +import { useAuthStore } from '../../store/AuthStore' +import Error from '../../../components/Error' +import Button from '../../../components/Button' +import { Link } from 'react-router-dom' +import { Pages } from '../../../config/Pages' + +const LoginStep1: FC = () => { + + const { t } = useTranslation('global') + const { setPhone, phone, setStepLogin } = useAuthStore() + + const formik = useFormik({ + initialValues: { + phone_email: phone, + }, + validationSchema: Yup.object({ + phone_email: Yup.string() + .required(t('errors.required')) + }), + onSubmit(values) { + setPhone(values.phone_email) + setStepLogin(2) + }, + }) + + return ( +
+
+

+ {t('auth.welcome')} +

+

+ {t('auth.enter_info_login')} +

+
+ +
+ + + { + formik.touched.phone_email && formik.errors.phone_email && + + } + +
+ + +
+ ) +} + +export default LoginStep1 \ No newline at end of file diff --git a/src/pages/auth/components/LoginStep2.tsx b/src/pages/auth/components/LoginStep2.tsx new file mode 100644 index 0000000..1cd07ad --- /dev/null +++ b/src/pages/auth/components/LoginStep2.tsx @@ -0,0 +1,91 @@ +import { FC, useState } from 'react' +import { useTranslation } from 'react-i18next' +import { LoginType } from '../types/AuthTypes' +import { useFormik } from 'formik' +import * as Yup from 'yup' +import { useAuthStore } from '../../store/AuthStore' +import Button from '../../../components/Button' +import OTPInput from 'react-otp-input' +import { useCountDown } from '../../../hooks/useCountDown' +import ArrowLeftIcon from '../../../assets/images/arrow-left.svg' + +const LoginStep2: FC = () => { + + const { t } = useTranslation('global') + const { setPhone, phone, setStepLogin } = useAuthStore() + const [otpValue, setOtpValue] = useState() + const { value } = useCountDown(2, true) + + const formik = useFormik({ + initialValues: { + phone_email: '', + }, + validationSchema: Yup.object({ + phone_email: Yup.string() + .required(t('errors.required')) + }), + onSubmit(values) { + setPhone(values.phone_email) + }, + }) + + return ( +
+
+

+ {t('auth.enter_otp')} +

+

+

{t('auth.verfify_code_text_1')}
+
{phone}
+
+ {t('auth.verfify_code_text_2')} +
+
setStepLogin(1)} className='text-black cursor-pointer'> + {t('auth.change_number')} +
+

+
+ +
+
+ {t('auth.verify_code')} +
+
+ setOtpValue(otp)} + renderInput={(props) => } + /> +
+ +
+
+
{value}
+
{t('auth.counter_otp')}
+
+
+ +
+ + +
+ ) +} + +export default LoginStep2 \ No newline at end of file diff --git a/src/pages/auth/components/LoginStep3.tsx b/src/pages/auth/components/LoginStep3.tsx new file mode 100644 index 0000000..dea5cb7 --- /dev/null +++ b/src/pages/auth/components/LoginStep3.tsx @@ -0,0 +1,84 @@ +import { FC } from 'react' +import Input from '../../../components/Input' +import { useTranslation } from 'react-i18next' +import { LoginPasswordType } from '../types/AuthTypes' +import { useFormik } from 'formik' +import * as Yup from 'yup' +import { useAuthStore } from '../../store/AuthStore' +import Error from '../../../components/Error' +import Button from '../../../components/Button' +import { Link } from 'react-router-dom' +import { Pages } from '../../../config/Pages' +import ArrowLeftIcon from '../../../assets/images/arrow-left.svg' + +const LoginStep3: FC = () => { + + const { t } = useTranslation('global') + const { setStepLogin } = useAuthStore() + + const formik = useFormik({ + initialValues: { + password: '', + }, + validationSchema: Yup.object({ + phone_email: Yup.string() + .required(t('errors.required')) + }), + onSubmit(values) { + console.log(values); + + }, + }) + + return ( +
+
+

+ {t('auth.enter_password_step3')} +

+

+ {t('auth.enter_your_password')} +

+
+ +
+ + + { + formik.touched.password && formik.errors.password && + + } + +
+ +
+ {t('auth.forgot_password')} +
+ + +
+ ) +} + +export default LoginStep3 \ No newline at end of file diff --git a/src/pages/auth/types/AuthTypes.ts b/src/pages/auth/types/AuthTypes.ts new file mode 100644 index 0000000..d3522b3 --- /dev/null +++ b/src/pages/auth/types/AuthTypes.ts @@ -0,0 +1,13 @@ +export type LoginType = { + phone_email: string; +}; +export type LoginPasswordType = { + password: string; +}; + +export type AuthStoreType = { + phone: string; + setPhone: (value: string) => void; + stepLogin: number; + setStepLogin: (value: number) => void; +}; diff --git a/src/pages/store/AuthStore.ts b/src/pages/store/AuthStore.ts new file mode 100644 index 0000000..6ba50c5 --- /dev/null +++ b/src/pages/store/AuthStore.ts @@ -0,0 +1,13 @@ +import { create } from "zustand"; +import { AuthStoreType } from "../auth/types/AuthTypes"; + +export const useAuthStore = create((set) => ({ + phone: "", + setPhone(value) { + set({ phone: value }); + }, + stepLogin: 1, + setStepLogin(value) { + set({ stepLogin: value }); + }, +}));