From e116fc7a03215724c43add8860f0268a1d6b396b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CZiyedB=E2=80=9D?= Date: Tue, 16 Feb 2021 20:37:04 +0000 Subject: [PATCH] add check on inputs --- package-lock.json | 44 +++++++ package.json | 5 +- src/App.tsx | 5 +- .../layouts/NavigationBar/NavigationBar.tsx | 2 +- src/pages/Login/Login.scss | 11 ++ src/pages/Login/Login.tsx | 109 +++++++++++------- src/services/interceptor/auth.ts | 2 +- 7 files changed, 129 insertions(+), 49 deletions(-) diff --git a/package-lock.json b/package-lock.json index 678ddc4..5f4b849 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1273,6 +1273,11 @@ "@hapi/hoek": "^8.3.0" } }, + "@hookform/resolvers": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/@hookform/resolvers/-/resolvers-1.3.4.tgz", + "integrity": "sha512-K56VLSInXNIT/r14pkzRn1FJclqzGOWqpe3Bf0kz2Hf98ZOmRRFh4fhB7F3ofqCQ03CEQQkV44CTg7ql6nEvEg==" + }, "@istanbuljs/load-nyc-config": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", @@ -2026,6 +2031,11 @@ "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", "integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=" }, + "@types/lodash": { + "version": "4.14.168", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.168.tgz", + "integrity": "sha512-oVfRvqHV/V6D1yifJbVRU3TMp8OT6o6BG+U9MkwuJ3U8/CsDHvalRpsxBqivn71ztOFZBTfJMvETbqHiaNSj7Q==" + }, "@types/minimatch": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz", @@ -10039,6 +10049,11 @@ "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.2.tgz", "integrity": "sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ==" }, + "nanoclone": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/nanoclone/-/nanoclone-0.2.1.tgz", + "integrity": "sha512-wynEP02LmIbLpcYw8uBKpcfF6dmg2vcpKqxeH5UcoKEYdExslsdUA4ugFauuaeYdTB76ez6gJW8XAZ6CgkXYxA==" + }, "nanoid": { "version": "3.1.20", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.20.tgz", @@ -12297,6 +12312,11 @@ "react-is": "^16.8.1" } }, + "property-expr": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/property-expr/-/property-expr-2.0.4.tgz", + "integrity": "sha512-sFPkHQjVKheDNnPvotjQmm3KD3uk1fWKUN7CrpdbwmUx3CrG3QiM8QpTSimvig5vTXmTvjz7+TDvXOI9+4rkcg==" + }, "proxy-addr": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz", @@ -12631,6 +12651,11 @@ "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.0.tgz", "integrity": "sha512-rtGImPZ0YyLrscKI9xTpV8psd6I8VAtjKCzQDlzyDvqJA8XOW78TXYQwNRNd8g8JZnDu8q9Fu/1v4HPAVwVdHA==" }, + "react-hook-form": { + "version": "6.15.1", + "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-6.15.1.tgz", + "integrity": "sha512-bL0LQuQ3OlM3JYfbacKtBPLOHhmgYz8Lj6ivMrvu2M6e1wnt4sbGRtPEPYCc/8z3WDbjrMwfAfLX92OsB65pFA==" + }, "react-icons": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-4.2.0.tgz", @@ -15153,6 +15178,11 @@ "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==" }, + "toposort": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/toposort/-/toposort-2.0.2.tgz", + "integrity": "sha1-riF2gXXRVZ1IvvNUILL0li8JwzA=" + }, "tough-cookie": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-3.0.1.tgz", @@ -17183,6 +17213,20 @@ "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==" + }, + "yup": { + "version": "0.32.8", + "resolved": "https://registry.npmjs.org/yup/-/yup-0.32.8.tgz", + "integrity": "sha512-SZulv5FIZ9d5H99EN5tRCRPXL0eyoYxWIP1AacCrjC9d4DfP13J1dROdKGfpfRHT3eQB6/ikBl5jG21smAfCkA==", + "requires": { + "@babel/runtime": "^7.10.5", + "@types/lodash": "^4.14.165", + "lodash": "^4.17.20", + "lodash-es": "^4.17.11", + "nanoclone": "^0.2.1", + "property-expr": "^2.0.4", + "toposort": "^2.0.2" + } } } } diff --git a/package.json b/package.json index 7fe32ab..d0bd862 100644 --- a/package.json +++ b/package.json @@ -3,6 +3,7 @@ "version": "0.1.0", "private": true, "dependencies": { + "@hookform/resolvers": "^1.3.4", "@testing-library/jest-dom": "^5.11.4", "@testing-library/react": "^11.1.0", "@testing-library/user-event": "^12.1.10", @@ -13,13 +14,15 @@ "node-sass": "^4.14.1", "react": "^17.0.1", "react-dom": "^17.0.1", + "react-hook-form": "^6.15.1", "react-icons": "^4.2.0", "react-router-dom": "^5.2.0", "react-scripts": "4.0.1", "semantic-ui-css": "^2.4.1", "semantic-ui-react": "^2.0.3", "typescript": "^4.1.3", - "web-vitals": "^0.2.4" + "web-vitals": "^0.2.4", + "yup": "^0.32.8" }, "scripts": { "start": "react-scripts start", diff --git a/src/App.tsx b/src/App.tsx index 1a81948..89ad187 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -11,8 +11,8 @@ import { ProtectedRoute } from './shared/ProtectedRoute'; function App(): JSX.Element { // Handle user state - // Ask if we should have this instead directly in a utils instead - const [isLoggedIn, setLoggedIn] = useState(false); + const accessToken = localStorage.getItem(storageNames.user) || ''; + const [isLoggedIn, setLoggedIn] = useState(!!accessToken); // Handle user login by setting the storage and state const login = (token: string): void => { @@ -26,7 +26,6 @@ function App(): JSX.Element { localStorage.removeItem(storageNames.user); }; - // TODO: still need to handle the first time getting to site ( if token still valid, present etc. ) return ( diff --git a/src/components/layouts/NavigationBar/NavigationBar.tsx b/src/components/layouts/NavigationBar/NavigationBar.tsx index 5145996..daf082b 100644 --- a/src/components/layouts/NavigationBar/NavigationBar.tsx +++ b/src/components/layouts/NavigationBar/NavigationBar.tsx @@ -32,7 +32,7 @@ const NavigationBar = (): JSX.Element => { {userContext.isLoggedIn ? (
{/* TODO: Need to have this as a small menu showing up */} - +
diff --git a/src/pages/Login/Login.scss b/src/pages/Login/Login.scss index 3eeb606..d12c6ab 100644 --- a/src/pages/Login/Login.scss +++ b/src/pages/Login/Login.scss @@ -1,5 +1,6 @@ $elevation-card: 0 0 1px 0 rgba(8, 11, 14, 0.6), 0 16px 16px -1px rgba(8, 11, 14, 0.1); $color-secondary: #e1e4e8; +$error-color: red; .login { &__card { @@ -34,6 +35,16 @@ $color-secondary: #e1e4e8; width: 100%; outline: none; padding: 0 15px; + + &--error { + border-color: $error-color; + } + } + + &__input-error-msg { + font-size: 12px; + margin-left: 15px; + color: $error-color; } &__input-icon { diff --git a/src/pages/Login/Login.tsx b/src/pages/Login/Login.tsx index 078c822..b1f8368 100644 --- a/src/pages/Login/Login.tsx +++ b/src/pages/Login/Login.tsx @@ -1,6 +1,9 @@ +import { yupResolver } from '@hookform/resolvers/yup'; import React, { useContext } from 'react'; +import { useForm } from 'react-hook-form'; import { FaEnvelope, FaEye, FaEyeSlash } from 'react-icons/fa'; import { Redirect, RouteComponentProps } from 'react-router-dom'; +import * as yup from 'yup'; import UserContext from '../../hooks/UserContext'; import paralinkApi from '../../services/interceptor'; import './Login.scss'; @@ -9,24 +12,36 @@ interface LoginProps { from: string; } +type LoginFormData = { + email: string; + password: string; +}; + +const loginSchema = yup.object().shape({ + email: yup.string().email().required(), + password: yup.string().required(), // We could define rules here for password if there are +}); + const Login = (props: RouteComponentProps<{}, {}, LoginProps>): JSX.Element => { const userContext = useContext(UserContext); + const { register, handleSubmit, errors } = useForm({ resolver: yupResolver(loginSchema) }); const [state, setState] = React.useState({ - email: '', - password: '', + // email: '', + // password: '', seePassword: false, }); const { from } = props.location.state || { from: { pathname: '/' } }; - const handleChange = (e: React.ChangeEvent): void => { - const { id, value } = e.target; - setState((prevState) => ({ - ...prevState, - [id]: value, - })); - }; + // const handleChange = (e: React.ChangeEvent): void => { + // const { id, value } = e.target; + // setState((prevState) => ({ + // ...prevState, + // [id]: value, + // })); + // console.log('errors', errors); + // }; const togglePassword = (): void => { setState((prevState) => ({ @@ -35,11 +50,14 @@ const Login = (props: RouteComponentProps<{}, {}, LoginProps>): JSX.Element => { })); }; - const login = async (): Promise => { + const login = async (data: LoginFormData): Promise => { + // Avoid the page to refresh from submitting the form + // event.preventDefault(); // TODO: put like a loading icon on the login button // TODO: do like a safe check on the email & password here + console.log('data', data); // Just to check await paralinkApi // For now this is going to be the placeholder @@ -66,41 +84,46 @@ const Login = (props: RouteComponentProps<{}, {}, LoginProps>): JSX.Element => { return (
Login into your account
-
- -
- +
+
+ +
+ +
+
+ {errors.email?.message ? {errors.email?.message} : ''} + +
+ + +
-
-
- - - -
- +
); }; diff --git a/src/services/interceptor/auth.ts b/src/services/interceptor/auth.ts index eaf339a..0cc1482 100644 --- a/src/services/interceptor/auth.ts +++ b/src/services/interceptor/auth.ts @@ -8,7 +8,7 @@ const paralinkApi = axios.create({ paralinkApi.interceptors.request.use( (config: any) => { - // Check for user token + // Check for user token, we can't use hooks from here const accessToken = localStorage.getItem(storageNames.user); // If user not logged we just pass it through, the backend should not accept it