From b0931bfb6c41041aa70856908ee792e297a10c77 Mon Sep 17 00:00:00 2001 From: wayneleon1 Date: Tue, 18 Jun 2024 23:33:46 +0200 Subject: [PATCH] setup envirnoment implement HSButton implement HSInput mend implement login design resolve eslint errors rebase from develop fix hovering styles button rebase form develop set up formik implement stage 1 of valifation usin formik Reducing boilerplate reduce duplicate codes --- .eslintrc.cjs | 2 + package-lock.json | 103 ++++++++++++++- package.json | 5 +- src/components/form/HSButton.tsx | 37 ++++++ src/components/form/HSInput.tsx | 72 ++++++++++ .../button => features/Auth/SignInSlice.tsx} | 0 src/index.css | 4 + src/pages/SignIn.tsx | 124 ++++++++++++++++++ src/routes/AppRoutes.tsx | 2 + 9 files changed, 343 insertions(+), 6 deletions(-) create mode 100644 src/components/form/HSButton.tsx create mode 100644 src/components/form/HSInput.tsx rename src/{components/button => features/Auth/SignInSlice.tsx} (100%) create mode 100644 src/pages/SignIn.tsx diff --git a/.eslintrc.cjs b/.eslintrc.cjs index 33762dcf..1d2b8c58 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -23,6 +23,8 @@ module.exports = { 'react/react-in-jsx-scope': 0, 'import/no-extraneous-dependencies': 0, 'import/extensions': 0, + 'react/require-default-props': 0, + 'react/self-closing-comp': 0, }, ignorePatterns: ['dist/**/*', 'postcss.config.js', 'tailwind.config.js'], }; diff --git a/package-lock.json b/package-lock.json index e28e7226..19cbb4b5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,11 +13,14 @@ "@types/react-router-dom": "^5.3.3", "axios": "^1.7.2", "dotenv": "^16.4.5", + "formik": "^2.4.6", "react": "^18.2.0", "react-dom": "^18.2.0", + "react-icons": "^5.2.1", "react-redux": "^9.1.2", "react-router-dom": "^6.23.1", - "redux": "^5.0.1" + "redux": "^5.0.1", + "yup": "^1.4.0" }, "devDependencies": { "@testing-library/jest-dom": "^6.4.5", @@ -3068,6 +3071,14 @@ "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "dev": true }, + "node_modules/deepmerge": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-2.2.1.tgz", + "integrity": "sha512-R9hc1Xa/NOBi9WRVUWg19rl1UB7Tt4kuPd+thNJgFZoxXsTz7ncaPaeIm+40oSGuP33DfMb4sZt1QIGiJzC4EA==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/define-data-property": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", @@ -4236,6 +4247,30 @@ "node": ">= 6" } }, + "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" + } + ], + "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", @@ -5606,8 +5641,12 @@ "node_modules/lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, + "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==" }, "node_modules/lodash.merge": { "version": "4.6.2", @@ -6640,6 +6679,11 @@ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", "dev": true }, + "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==" + }, "node_modules/proxy-from-env": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", @@ -6709,6 +6753,19 @@ "react": "^18.3.1" } }, + "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==" + }, + "node_modules/react-icons": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-5.2.1.tgz", + "integrity": "sha512-zdbW5GstTzXaVKvGSyTaBalt7HSfuK5ovrzlpyiWHAFXndXTdd/1hdDHI4xBM1Mn7YriT6aqESucFl9kEXzrdw==", + "peerDependencies": { + "react": "*" + } + }, "node_modules/react-is": { "version": "17.0.2", "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", @@ -7707,6 +7764,16 @@ "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==" + }, + "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==" + }, "node_modules/tinybench": { "version": "2.8.0", "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.8.0.tgz", @@ -7752,6 +7819,11 @@ "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==" + }, "node_modules/tough-cookie": { "version": "4.1.4", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.4.tgz", @@ -7826,8 +7898,7 @@ "node_modules/tslib": { "version": "2.6.3", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", - "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==", - "dev": true + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" }, "node_modules/type-check": { "version": "0.4.0", @@ -8528,6 +8599,28 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } + }, + "node_modules/yup": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/yup/-/yup-1.4.0.tgz", + "integrity": "sha512-wPbgkJRCqIf+OHyiTBQoJiP5PFuAXaWiJK6AmYkzQAh5/c2K9hzSApBZG5wV9KoKSePF7sAxmNSvh/13YHkFDg==", + "dependencies": { + "property-expr": "^2.0.5", + "tiny-case": "^1.0.3", + "toposort": "^2.0.2", + "type-fest": "^2.19.0" + } + }, + "node_modules/yup/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==", + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } } } } diff --git a/package.json b/package.json index 3a0a7c04..42ec6a12 100644 --- a/package.json +++ b/package.json @@ -21,11 +21,14 @@ "@types/react-router-dom": "^5.3.3", "axios": "^1.7.2", "dotenv": "^16.4.5", + "formik": "^2.4.6", "react": "^18.2.0", "react-dom": "^18.2.0", + "react-icons": "^5.2.1", "react-redux": "^9.1.2", "react-router-dom": "^6.23.1", - "redux": "^5.0.1" + "redux": "^5.0.1", + "yup": "^1.4.0" }, "devDependencies": { "@testing-library/jest-dom": "^6.4.5", diff --git a/src/components/form/HSButton.tsx b/src/components/form/HSButton.tsx new file mode 100644 index 00000000..679055ad --- /dev/null +++ b/src/components/form/HSButton.tsx @@ -0,0 +1,37 @@ +import { Link } from 'react-router-dom'; + +interface MyButtonProps { + path?: string; + title: string; + styles?: string; + click?: () => void; + icon?: JSX.Element; + target?: '_blank' | '_self' | '_parent' | '_top'; + onChange?: React.ChangeEventHandler; +} + +function HSButton({ + path, + click, + title, + icon, + styles, + target, + onChange, +}: MyButtonProps) { + return ( + + {title} {icon} + + ); +} + +export default HSButton; diff --git a/src/components/form/HSInput.tsx b/src/components/form/HSInput.tsx new file mode 100644 index 00000000..9eb42c6d --- /dev/null +++ b/src/components/form/HSInput.tsx @@ -0,0 +1,72 @@ +interface MyInputProps { + id?: string; + name?: string; + onBlur?: (event: React.FocusEvent) => void; + onBlurTextArea?: (event: React.FocusEvent) => void; + values?: string | number; + style?: string; + label?: string; + onChange?: (event: React.ChangeEvent) => void; + onChangeTextArea?: (event: React.ChangeEvent) => void; + placeholder: string; + type?: string; + text?: string; + icon?: JSX.Element; +} + +function HSInput({ + id, + name, + onBlur, + onBlurTextArea, + values, + style, + label, + onChange, + onChangeTextArea, + placeholder, + type, + text, + icon, +}: MyInputProps) { + return ( +
+ + {type === 'input' ? ( +
+ {icon &&

{icon}

} + +
+ ) : ( + + )} +
+ ); +} + +export default HSInput; diff --git a/src/components/button b/src/features/Auth/SignInSlice.tsx similarity index 100% rename from src/components/button rename to src/features/Auth/SignInSlice.tsx diff --git a/src/index.css b/src/index.css index b5c61c95..71d3c0a5 100644 --- a/src/index.css +++ b/src/index.css @@ -1,3 +1,7 @@ @tailwind base; @tailwind components; @tailwind utilities; +@import url('https://fonts.googleapis.com/css2?family=Lexend:wght@100..900&display=swap'); +* { + font-family: 'Lexend', sans-serif; +} diff --git a/src/pages/SignIn.tsx b/src/pages/SignIn.tsx new file mode 100644 index 00000000..c9725e0a --- /dev/null +++ b/src/pages/SignIn.tsx @@ -0,0 +1,124 @@ +import { Link } from 'react-router-dom'; +import { ErrorMessage, useFormik } from 'formik'; +import * as yup from 'yup'; +import { MdOutlineEmail } from 'react-icons/md'; +import { PiLockKeyBold } from 'react-icons/pi'; +import { FcGoogle } from 'react-icons/fc'; +import { FaFacebook } from 'react-icons/fa'; +import HSButton from '@/components/form/HSButton'; +import HSInput from '@/components/form/HSInput'; + +interface MyFormValues { + email: string; + password: string; +} + +const initialValues: MyFormValues = { + email: '', + password: '', +}; + +const onSubmit = (values: MyFormValues) => { + console.log('Form data:', values); +}; + +const validationSchema: yup.ObjectSchema = yup.object({ + email: yup + .string() + .email('Invalid email format') + .required('Email is required!'), + password: yup + .string() + .min(6, 'Password must contain at least 6 chars') + .required('Password is required!'), +}); + +function SignIn() { + const formik = useFormik({ + initialValues, + onSubmit, + validationSchema, + }); + + return ( +
+
+

Sign in

+
+
+ } + {...formik.getFieldProps('email')} + /> + {formik.touched.email && formik.errors.email ? ( +
+ {formik.errors.email} +
+ ) : null} +
+
+ } + {...formik.getFieldProps('password')} + /> + {formik.touched.password && formik.errors.password ? ( +
+ {formik.errors.password} +
+ ) : null} +
+
+
+ +

Remember me

+
+
+ + Forgot password? + +
+
+ +
+

+ Don't have an account?{' '} + + Sign up + +

+
+
+
+

OR

+
+
+
+
+ +
+
+ +
+
+
+
+
+ ); +} + +export default SignIn; diff --git a/src/routes/AppRoutes.tsx b/src/routes/AppRoutes.tsx index 08b03535..04e7c725 100644 --- a/src/routes/AppRoutes.tsx +++ b/src/routes/AppRoutes.tsx @@ -2,6 +2,7 @@ import { BrowserRouter as Router, Route, Routes } from 'react-router-dom'; import HomeLayout from '@/layout/HomeLayout'; import Home from '@/pages/Home'; import ErrorPage from '@/pages/ErrorPage'; +import SignIn from '@/pages/SignIn'; function AppRoutes() { return ( @@ -10,6 +11,7 @@ function AppRoutes() { }> } /> + } /> } /> {/* Add many routes as you want */}