From 8d73f7cabc05e4864ac9b9fe46a9e5ae0eea7d43 Mon Sep 17 00:00:00 2001 From: FadhilP <fadhilpradipta0@gmail.com> Date: Thu, 7 Jan 2021 13:50:46 +0700 Subject: [PATCH 01/11] add auth.service --- diskuy/src/services/auth.service.js | 31 +++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 diskuy/src/services/auth.service.js diff --git a/diskuy/src/services/auth.service.js b/diskuy/src/services/auth.service.js new file mode 100644 index 0000000..6e0af95 --- /dev/null +++ b/diskuy/src/services/auth.service.js @@ -0,0 +1,31 @@ +import axios from 'axios' + +const API_URL = 'http://localhost:4000/api/auth' + +class AuthService { + async login(username, password) { + const response = await axios.post(`${API_URL}/signin`, { + username, + password + }) + if (response.data.accessToken) + localStorage.setItem('user', JSON.stringify(response.data)) + + return response + } + logout() {localStorage.removeItem('user')} + + register(username, email, password) { + return axios.post(`${API_URL}/signup`, { + username, + email, + password + }) + } + + getCurrentUser() { + return JSON.parse(localStorage.getItem('user')) + } +} + +export default new AuthService() \ No newline at end of file -- GitLab From 1f83d9d30d383ac8516a74da456ed484d18b1e38 Mon Sep 17 00:00:00 2001 From: FadhilP <fadhilpradipta0@gmail.com> Date: Thu, 7 Jan 2021 13:55:39 +0700 Subject: [PATCH 02/11] add auth-header --- diskuy/src/services/auth-header.js | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 diskuy/src/services/auth-header.js diff --git a/diskuy/src/services/auth-header.js b/diskuy/src/services/auth-header.js new file mode 100644 index 0000000..e9c1ad4 --- /dev/null +++ b/diskuy/src/services/auth-header.js @@ -0,0 +1,7 @@ +export default function authHeader() { + const user = JSON.parse(localStorage.getItem('user')) + + if (user && user.accessToken) + return { Authorization: 'Bearer' + user.accessToken } + else return {} +} \ No newline at end of file -- GitLab From dd3e51dd53193ee604846f80ff5ad03cf0e03e2d Mon Sep 17 00:00:00 2001 From: FadhilP <fadhilpradipta0@gmail.com> Date: Thu, 7 Jan 2021 14:03:21 +0700 Subject: [PATCH 03/11] add user.service --- diskuy/src/services/user.service.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 diskuy/src/services/user.service.js diff --git a/diskuy/src/services/user.service.js b/diskuy/src/services/user.service.js new file mode 100644 index 0000000..b1bc801 --- /dev/null +++ b/diskuy/src/services/user.service.js @@ -0,0 +1,12 @@ +import axios from 'axios' +import authHeader from './auth-header' + +const API_URL = 'http://localhost:4000/api' + +class UserService { + getProfile() { + return axios.get(`${API_URL}/user`, { headers : authHeader()}) + } +} + +export default new UserService() \ No newline at end of file -- GitLab From c7a75d25f151b44b83f0b88800422bd79764160d Mon Sep 17 00:00:00 2001 From: FadhilP <fadhilpradipta0@gmail.com> Date: Thu, 7 Jan 2021 14:25:46 +0700 Subject: [PATCH 04/11] add react form validator lib --- diskuy/package-lock.json | 44 ++++++++++++++++++++++++++++++++++++++++ diskuy/package.json | 2 ++ 2 files changed, 46 insertions(+) diff --git a/diskuy/package-lock.json b/diskuy/package-lock.json index ce0783d..0221d74 100644 --- a/diskuy/package-lock.json +++ b/diskuy/package-lock.json @@ -9681,6 +9681,11 @@ "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", "integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=" }, + "lodash.omit": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.omit/-/lodash.omit-4.5.0.tgz", + "integrity": "sha1-brGa5aHuHdnfC5aeZs4Lf6MLXmA=" + }, "lodash.sortby": { "version": "4.7.0", "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", @@ -12465,6 +12470,35 @@ "workbox-webpack-plugin": "5.1.4" } }, + "react-validation": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/react-validation/-/react-validation-3.0.7.tgz", + "integrity": "sha1-tQcL+KbnN7hw2Hu/tyzMpys/N1A=", + "requires": { + "lodash.omit": "^4.5.0", + "prop-types": "^15.6.0", + "react": "^16.0.0", + "shallow-equal": "^1.0.0", + "uuid": "^3.1.0" + }, + "dependencies": { + "react": { + "version": "16.14.0", + "resolved": "https://registry.npmjs.org/react/-/react-16.14.0.tgz", + "integrity": "sha512-0X2CImDkJGApiAlcf0ODKIneSwBPhqJawOa5wCtKbu7ZECrmS26NvtSILynQ66cgkT/RJ4LidJOc3bUESwmU8g==", + "requires": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1", + "prop-types": "^15.6.2" + } + }, + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" + } + } + }, "read-pkg": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", @@ -13513,6 +13547,11 @@ "kind-of": "^6.0.2" } }, + "shallow-equal": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/shallow-equal/-/shallow-equal-1.2.1.tgz", + "integrity": "sha512-S4vJDjHHMBaiZuT9NPb616CSmLf618jawtv3sufLl6ivK8WocjAo58cXwbRV1cgqxH0Qbv+iUt6m05eqEa2IRA==" + }, "shebang-command": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", @@ -15007,6 +15046,11 @@ "spdx-expression-parse": "^3.0.0" } }, + "validator": { + "version": "13.5.2", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.5.2.tgz", + "integrity": "sha512-mD45p0rvHVBlY2Zuy3F3ESIe1h5X58GPfAtslBjY7EtTqGquZTj+VX/J4RnHWN8FKq0C9WRVt1oWAcytWRuYLQ==" + }, "value-equal": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/value-equal/-/value-equal-1.0.1.tgz", diff --git a/diskuy/package.json b/diskuy/package.json index 3c8f1e3..41b045f 100644 --- a/diskuy/package.json +++ b/diskuy/package.json @@ -11,6 +11,8 @@ "react-dom": "^17.0.1", "react-router-dom": "^5.2.0", "react-scripts": "4.0.1", + "react-validation": "^3.0.7", + "validator": "^13.5.2", "web-vitals": "^0.2.4" }, "scripts": { -- GitLab From de53e193e291af0a5991d599dce50db78cd9012a Mon Sep 17 00:00:00 2001 From: FadhilP <fadhilpradipta0@gmail.com> Date: Thu, 7 Jan 2021 14:26:18 +0700 Subject: [PATCH 05/11] add Login component pre req --- diskuy/src/Login.js | 48 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 diskuy/src/Login.js diff --git a/diskuy/src/Login.js b/diskuy/src/Login.js new file mode 100644 index 0000000..ac7c150 --- /dev/null +++ b/diskuy/src/Login.js @@ -0,0 +1,48 @@ +import React from 'react' +import Form from "react-validation/bulid/form" +import Input from "react-validation/bulid/input" +import CheckButton from "react-validation/bulid/button" +import { isEmail } from 'validator' +import { useInput } from './hooks/input-hook'; +import { useState } from "react"; + +import AuthService from './services/auth.service' + +const required = value => { + if (!value) { + return ( + <div className='alert alert-danger' role='alert'> + This field is required! + </div> + ) + } +} + +export default function Login(props){ + const [username, bindUsername, resetUsername] = useInput('') + const [password, bindPassword, resetPassword] = useInput('') + const [loading, setLoading] = useState(false) + const [message, setMessage] = useState('') + + const handleLogin = async event => { + event.preventDefault() + setLoading(true) + + this.form.validateAll() + + if (this.checkBtn.context._errors.length === 0) { + try { + await AuthService.login(username, password) + // ? this.props.history.push('/profile') + // ? window.location.reload() + } + catch(error) { + const responseMessage = + (error.response && error.response.data && error.response.data.message) || error.message || error.toString() + + setMessage(responseMessage) + } + } + setLoading(false) + } +} -- GitLab From 90310fdff1e8b12c89c21e401563f555d889923b Mon Sep 17 00:00:00 2001 From: FadhilP <fadhilpradipta0@gmail.com> Date: Thu, 7 Jan 2021 14:38:17 +0700 Subject: [PATCH 06/11] add login route --- diskuy/src/App.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/diskuy/src/App.js b/diskuy/src/App.js index 0f77f60..9a907b8 100644 --- a/diskuy/src/App.js +++ b/diskuy/src/App.js @@ -5,6 +5,7 @@ import Navbar from './Navbar'; import Profile from './Profile/Profile'; import ListThreads from './Threads/ListThreads'; import Form from './Form'; +import Login from './Login' import TopicList from './TopicList'; import { BrowserRouter as Router, @@ -19,6 +20,7 @@ function App() { <Navbar /> <Route exact path='/' component={TopicList} /> <Route exact path='/profile' component={Profile} /> + <Route exact path='/login' component={Login} /> <Route exact path='/topic/:topic/thread/create' component={Form} /> <Route exact path='/topic/:topic/:thread' component={Thread} /> <Route exact path='/topic/:topic' component={Topic} /> -- GitLab From c93b04d75167c0bd06febd599ae401e332dd5513 Mon Sep 17 00:00:00 2001 From: FadhilP <fadhilpradipta0@gmail.com> Date: Thu, 7 Jan 2021 14:38:40 +0700 Subject: [PATCH 07/11] add Login page render --- diskuy/src/Login.js | 52 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 51 insertions(+), 1 deletion(-) diff --git a/diskuy/src/Login.js b/diskuy/src/Login.js index ac7c150..442dcc0 100644 --- a/diskuy/src/Login.js +++ b/diskuy/src/Login.js @@ -39,10 +39,60 @@ export default function Login(props){ catch(error) { const responseMessage = (error.response && error.response.data && error.response.data.message) || error.message || error.toString() - setMessage(responseMessage) } } setLoading(false) } + + return ( + <div> + <Form + onSubmit={handleLogin} + ref={c => {this.form = c}} + > + <div className='form-group'> + <label htmlFor='username'>Username</label> + <Input + type='text' + className='form-control' + name='username' + {...bindUsername} + validations={[required]} + /> + </div> + <div className='form-group'> + <label htmlFor='password'>Password</label> + <Input + type='password' + className='form-control' + name='password' + {...bindPassword} + validations={[required]} + /> + </div> + + <div className='form-group'> + <button className='btn btn-primary btn-block' disabled={loading}> + {loading && ( + <span className='spinner-border spinner-border-sm'></span> + )} + <span>Login</span> + </button> + </div> + + {message && ( + <div className='form-group'> + <div className='alert alert-danger' role='alert'> + {message} + </div> + </div> + )} + <CheckButton + style={{ display: 'none' }} + ref={c => {this.checkBtn = c}} + /> + </Form> + </div> + ) } -- GitLab From 236d55d0f228c34922f1b5668ec3c8c866825ffc Mon Sep 17 00:00:00 2001 From: FadhilP <fadhilpradipta0@gmail.com> Date: Thu, 7 Jan 2021 14:49:51 +0700 Subject: [PATCH 08/11] fix react-validation import --- diskuy/src/Login.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/diskuy/src/Login.js b/diskuy/src/Login.js index 442dcc0..a80d647 100644 --- a/diskuy/src/Login.js +++ b/diskuy/src/Login.js @@ -1,13 +1,13 @@ import React from 'react' -import Form from "react-validation/bulid/form" -import Input from "react-validation/bulid/input" -import CheckButton from "react-validation/bulid/button" +import { form, input, button } from 'react-validation' import { isEmail } from 'validator' import { useInput } from './hooks/input-hook'; import { useState } from "react"; import AuthService from './services/auth.service' +const [Form, Input, CheckButton] = [form, input, button] + const required = value => { if (!value) { return ( -- GitLab From 5369e73372a3e15c654efdb872bf867a8fe97eb2 Mon Sep 17 00:00:00 2001 From: FadhilP <fadhilpradipta0@gmail.com> Date: Thu, 7 Jan 2021 15:10:35 +0700 Subject: [PATCH 09/11] remove react-validation, resolve LoginForm errors --- diskuy/package-lock.json | 39 ---------------- diskuy/package.json | 1 - diskuy/src/App.js | 4 +- diskuy/src/Login.js | 98 ---------------------------------------- diskuy/src/LoginForm.js | 82 +++++++++++++++++++++++++++++++++ 5 files changed, 84 insertions(+), 140 deletions(-) delete mode 100644 diskuy/src/Login.js create mode 100644 diskuy/src/LoginForm.js diff --git a/diskuy/package-lock.json b/diskuy/package-lock.json index 0221d74..1182450 100644 --- a/diskuy/package-lock.json +++ b/diskuy/package-lock.json @@ -9681,11 +9681,6 @@ "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", "integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=" }, - "lodash.omit": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.omit/-/lodash.omit-4.5.0.tgz", - "integrity": "sha1-brGa5aHuHdnfC5aeZs4Lf6MLXmA=" - }, "lodash.sortby": { "version": "4.7.0", "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", @@ -12470,35 +12465,6 @@ "workbox-webpack-plugin": "5.1.4" } }, - "react-validation": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/react-validation/-/react-validation-3.0.7.tgz", - "integrity": "sha1-tQcL+KbnN7hw2Hu/tyzMpys/N1A=", - "requires": { - "lodash.omit": "^4.5.0", - "prop-types": "^15.6.0", - "react": "^16.0.0", - "shallow-equal": "^1.0.0", - "uuid": "^3.1.0" - }, - "dependencies": { - "react": { - "version": "16.14.0", - "resolved": "https://registry.npmjs.org/react/-/react-16.14.0.tgz", - "integrity": "sha512-0X2CImDkJGApiAlcf0ODKIneSwBPhqJawOa5wCtKbu7ZECrmS26NvtSILynQ66cgkT/RJ4LidJOc3bUESwmU8g==", - "requires": { - "loose-envify": "^1.1.0", - "object-assign": "^4.1.1", - "prop-types": "^15.6.2" - } - }, - "uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" - } - } - }, "read-pkg": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", @@ -13547,11 +13513,6 @@ "kind-of": "^6.0.2" } }, - "shallow-equal": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/shallow-equal/-/shallow-equal-1.2.1.tgz", - "integrity": "sha512-S4vJDjHHMBaiZuT9NPb616CSmLf618jawtv3sufLl6ivK8WocjAo58cXwbRV1cgqxH0Qbv+iUt6m05eqEa2IRA==" - }, "shebang-command": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", diff --git a/diskuy/package.json b/diskuy/package.json index 41b045f..3d5998a 100644 --- a/diskuy/package.json +++ b/diskuy/package.json @@ -11,7 +11,6 @@ "react-dom": "^17.0.1", "react-router-dom": "^5.2.0", "react-scripts": "4.0.1", - "react-validation": "^3.0.7", "validator": "^13.5.2", "web-vitals": "^0.2.4" }, diff --git a/diskuy/src/App.js b/diskuy/src/App.js index 9a907b8..c45caf6 100644 --- a/diskuy/src/App.js +++ b/diskuy/src/App.js @@ -5,7 +5,7 @@ import Navbar from './Navbar'; import Profile from './Profile/Profile'; import ListThreads from './Threads/ListThreads'; import Form from './Form'; -import Login from './Login' +import LoginForm from './LoginForm' import TopicList from './TopicList'; import { BrowserRouter as Router, @@ -20,7 +20,7 @@ function App() { <Navbar /> <Route exact path='/' component={TopicList} /> <Route exact path='/profile' component={Profile} /> - <Route exact path='/login' component={Login} /> + <Route exact path='/login' component={LoginForm} /> <Route exact path='/topic/:topic/thread/create' component={Form} /> <Route exact path='/topic/:topic/:thread' component={Thread} /> <Route exact path='/topic/:topic' component={Topic} /> diff --git a/diskuy/src/Login.js b/diskuy/src/Login.js deleted file mode 100644 index a80d647..0000000 --- a/diskuy/src/Login.js +++ /dev/null @@ -1,98 +0,0 @@ -import React from 'react' -import { form, input, button } from 'react-validation' -import { isEmail } from 'validator' -import { useInput } from './hooks/input-hook'; -import { useState } from "react"; - -import AuthService from './services/auth.service' - -const [Form, Input, CheckButton] = [form, input, button] - -const required = value => { - if (!value) { - return ( - <div className='alert alert-danger' role='alert'> - This field is required! - </div> - ) - } -} - -export default function Login(props){ - const [username, bindUsername, resetUsername] = useInput('') - const [password, bindPassword, resetPassword] = useInput('') - const [loading, setLoading] = useState(false) - const [message, setMessage] = useState('') - - const handleLogin = async event => { - event.preventDefault() - setLoading(true) - - this.form.validateAll() - - if (this.checkBtn.context._errors.length === 0) { - try { - await AuthService.login(username, password) - // ? this.props.history.push('/profile') - // ? window.location.reload() - } - catch(error) { - const responseMessage = - (error.response && error.response.data && error.response.data.message) || error.message || error.toString() - setMessage(responseMessage) - } - } - setLoading(false) - } - - return ( - <div> - <Form - onSubmit={handleLogin} - ref={c => {this.form = c}} - > - <div className='form-group'> - <label htmlFor='username'>Username</label> - <Input - type='text' - className='form-control' - name='username' - {...bindUsername} - validations={[required]} - /> - </div> - <div className='form-group'> - <label htmlFor='password'>Password</label> - <Input - type='password' - className='form-control' - name='password' - {...bindPassword} - validations={[required]} - /> - </div> - - <div className='form-group'> - <button className='btn btn-primary btn-block' disabled={loading}> - {loading && ( - <span className='spinner-border spinner-border-sm'></span> - )} - <span>Login</span> - </button> - </div> - - {message && ( - <div className='form-group'> - <div className='alert alert-danger' role='alert'> - {message} - </div> - </div> - )} - <CheckButton - style={{ display: 'none' }} - ref={c => {this.checkBtn = c}} - /> - </Form> - </div> - ) -} diff --git a/diskuy/src/LoginForm.js b/diskuy/src/LoginForm.js new file mode 100644 index 0000000..3a5f873 --- /dev/null +++ b/diskuy/src/LoginForm.js @@ -0,0 +1,82 @@ +import React from 'react' +import { isEmail } from 'validator' +import { useInput } from './hooks/input-hook'; +import { useState } from "react"; + +import AuthService from './services/auth.service' + +const required = value => { + if (!value) { + return ( + <div className='alert alert-danger' role='alert'> + This field is required! + </div> + ) + } +} + +export default function LoginForm(){ + + const { value: username, bind: bindUsername, reset: resetUsername } = useInput('') + const { value: password, bind: bindPassword, reset: resetPassword } = useInput('') + const [loading, setLoading] = useState(false) + const [message, setMessage] = useState('') + + const handleLogin = async event => { + event.preventDefault() + setLoading(true) + + try { + await AuthService.login(username, password) + // ? this.props.history.push('/profile') + // ? window.location.reload() + } + catch(error) { + const responseMessage = + (error.response && error.response.data && error.response.data.message) || error.message || error.toString() + setMessage(responseMessage) + } + setLoading(false) + } + + return ( + <div> + <form onSubmit={handleLogin}> + <label htmlFor='username'>Username</label> + <input + type='text' + name='username' + {...bindUsername} + validations={[required]} + /> + + <label htmlFor='password'>Password</label> + <input + type='password' + name='password' + {...bindPassword} + validations={[required]} + /> + + <button className='btn btn-primary' disabled={loading}> + {loading && ( + <span className='spinner-border spinner-border-sm'></span> + )} + <span>Login</span> + </button> + + {message && ( + <div className='form-group'> + <div className='alert alert-danger' role='alert'> + {message} + </div> + </div> + )} + <button + style={{ display: 'none' }} + // ref={c => {this.checkBtn = c}} + /> + </form> + </div> + ) +} -- GitLab From 6e7e82e690d32d7f0e1fbc6d583625ade8caf215 Mon Sep 17 00:00:00 2001 From: FadhilP <fadhilpradipta0@gmail.com> Date: Thu, 7 Jan 2021 15:11:25 +0700 Subject: [PATCH 10/11] redirect after successful login --- diskuy/src/LoginForm.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/diskuy/src/LoginForm.js b/diskuy/src/LoginForm.js index 3a5f873..9caa864 100644 --- a/diskuy/src/LoginForm.js +++ b/diskuy/src/LoginForm.js @@ -28,8 +28,7 @@ export default function LoginForm(){ try { await AuthService.login(username, password) - // ? this.props.history.push('/profile') - // ? window.location.reload() + this.props.history.push('/topics') } catch(error) { const responseMessage = -- GitLab From 7d5e7de0cb1861b3aa075408144a3eef86b4c76c Mon Sep 17 00:00:00 2001 From: FadhilP <fadhilpradipta0@gmail.com> Date: Thu, 7 Jan 2021 15:13:00 +0700 Subject: [PATCH 11/11] reset field when error, remove unnecessary component in render --- diskuy/src/LoginForm.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/diskuy/src/LoginForm.js b/diskuy/src/LoginForm.js index 9caa864..8dec4cc 100644 --- a/diskuy/src/LoginForm.js +++ b/diskuy/src/LoginForm.js @@ -34,6 +34,8 @@ export default function LoginForm(){ const responseMessage = (error.response && error.response.data && error.response.data.message) || error.message || error.toString() setMessage(responseMessage) + resetPassword() + resetUsername() } setLoading(false) } @@ -71,10 +73,6 @@ export default function LoginForm(){ </div> </div> )} - <button - style={{ display: 'none' }} - // ref={c => {this.checkBtn = c}} - /> </form> </div> ) -- GitLab