송용우

Update Login/Register

1 +import React from 'react';
2 +import styled from 'styled-components';
3 +import { Link } from 'react-router-dom';
4 +import Button from '../common/Button';
5 +import palette from '../../lib/styles/palette';
6 +/*
7 +Display Auth Form(Register, Login)
8 +*/
9 +
10 +const AuthFormBlock = styled.div`
11 + h3 {
12 + margin: 0;
13 + color: ${palette.gray[8]};
14 + margin-bottom: 1rem;
15 + }
16 +`;
17 +
18 +const StyledInput = styled.input`
19 + font-size: 1rem;
20 + border: none;
21 + border-bottom: 1px solid ${palette.gray[5]};
22 + padding-bottom: 0.5rem;
23 + outline: none;
24 + width: 100%;
25 + &:focus {
26 + color: $oc-teal-7;
27 + border-bottom: 1px solid ${palette.gray[7]};
28 + }
29 + & + & {
30 + margin-top: 1rem;
31 + }
32 +`;
33 +
34 +const Footer = styled.div`
35 + margin-top: 2rem;
36 + text-align: right;
37 + a {
38 + color: ${palette.gray[6]};
39 + text-decoration: underline;
40 + &:hover {
41 + color: ${palette.gray[9]};
42 + }
43 + }
44 +`;
45 +
46 +const ButtonWithMarginTop = styled(Button)`
47 + margin-top: 1rem;
48 +`;
49 +
50 +/**
51 + * Show Error message
52 + */
53 +const ErrorMessage = styled.div`
54 + color: red;
55 + text-align: center;
56 + font-size: 0.875rem;
57 + margin-top: 1rem;
58 +`;
59 +
60 +const textMap = {
61 + login: '로그인',
62 + register: '회원가입',
63 +};
64 +
65 +const AuthForm = ({ type, form, onChange, onSubmit, error }) => {
66 + const text = textMap[type];
67 + return (
68 + <AuthFormBlock>
69 + <h3>{text}</h3>
70 + <form onSubmit={onSubmit}>
71 + <StyledInput
72 + autoComplete="username"
73 + name="username"
74 + placeholder="아이디"
75 + onChange={onChange}
76 + value={form.username}
77 + />
78 + <StyledInput
79 + autoComplete="new-password"
80 + name="password"
81 + placeholder="비밀번호"
82 + type="password"
83 + onChange={onChange}
84 + value={form.password}
85 + />
86 + {type === 'register' && (
87 + <StyledInput
88 + autoComplete="new-password"
89 + name="passwordConfirm"
90 + placeholder="비밀번호 확인"
91 + type="password"
92 + onChange={onChange}
93 + value={form.passwordConfirm}
94 + />
95 + )}
96 + {error && <ErrorMessage>{error}</ErrorMessage>}
97 + <ButtonWithMarginTop cyan fullWidth>
98 + {text}
99 + </ButtonWithMarginTop>
100 + </form>
101 + <Footer>
102 + {type === 'login' ? (
103 + <Link to="/register">회원가입</Link>
104 + ) : (
105 + <Link to="/login">로그인</Link>
106 + )}
107 + </Footer>
108 + </AuthFormBlock>
109 + );
110 +};
111 +
112 +export default AuthForm;
1 +import React from 'react';
2 +import styled from 'styled-components';
3 +import palette from '../../lib/styles/palette';
4 +import { Link } from 'react-router-dom';
5 +/*
6 +register/login Layout
7 +*/
8 +const AuthTemplateBlock = styled.div`
9 + position: absolute;
10 + left: 0;
11 + top: 0;
12 + bottom: 0;
13 + right: 0;
14 + background: ${palette.gray[2]};
15 + display: flex;
16 + flex-direction: column;
17 + justify-content: center;
18 + align-items: center;
19 +`;
20 +
21 +const WhiteBox = styled.div`
22 + .logo-area {
23 + display: block;
24 + padding-bottom: 2rem;
25 + text-align: center;
26 + font-weight: bold;
27 + letter-spacing: 2px;
28 + }
29 + box-shadow: 0 0 8px rgba(0, 0, 0, 0.025);
30 + padding: 2rem;
31 + width: 360px;
32 + background: white;
33 + border-radius: 2px;
34 +`;
35 +
36 +const AuthTemplate = ({ children }) => {
37 + return (
38 + <AuthTemplateBlock>
39 + <WhiteBox>
40 + <div className="logo-area">
41 + <Link to="/">Jaksimsamil</Link>
42 + </div>
43 + {children}
44 + </WhiteBox>
45 + </AuthTemplateBlock>
46 + );
47 +};
48 +
49 +export default AuthTemplate;
1 import React from 'react'; 1 import React from 'react';
2 -import styled from 'styled-components'; 2 +import styled, { css } from 'styled-components';
3 import palette from '../../lib/styles/palette'; 3 import palette from '../../lib/styles/palette';
4 +import { withRouter } from 'react-router-dom';
4 5
5 const StyledButton = styled.button` 6 const StyledButton = styled.button`
6 border: none; 7 border: none;
...@@ -12,13 +13,38 @@ const StyledButton = styled.button` ...@@ -12,13 +13,38 @@ const StyledButton = styled.button`
12 outline: none; 13 outline: none;
13 cursor: pointer; 14 cursor: pointer;
14 15
15 - background: ${palette.gray[7]}; 16 + background: ${palette.gray[8]};
16 -
17 &:hover { 17 &:hover {
18 - background: ${palette.gray[5]}; 18 + background: ${palette.gray[6]};
19 } 19 }
20 -`; 20 + ${props =>
21 + props.fullWidth &&
22 + css`
23 + padding-top: 0.75rem;
24 + padding-bottom: 0.75rem;
25 + width: 100%;
26 + font-size: 1.125rem;
27 + `}
21 28
22 -const Button = (props) => <StyledButton {...props} />; 29 + ${props =>
30 + props.cyan &&
31 + css`
32 + background: ${palette.cyan[5]};
33 + &:hover {
34 + background: ${palette.cyan[4]};
35 + }
36 + `}
37 +`;
23 38
24 -export default Button; 39 +const Button = ({ to, history, ...rest }) => {
40 + const onClick = e => {
41 + if (to) {
42 + history.push(to);
43 + }
44 + if (rest.onClick) {
45 + rest.onClick(e);
46 + }
47 + };
48 + return <StyledButton {...rest} onClick={onClick} />;
49 +};
50 +export default withRouter(Button);
......
1 -import React from 'react';
2 -import styled from 'styled-components';
3 -/*
4 -Display Auth Form(Register, Login)
5 -*/
6 -const AuthFormBlock = styled.div``;
7 -
8 -const AuthForm = () => {
9 - return <AuthFormBlock>AuthForm</AuthFormBlock>;
10 -};
11 -
12 -export default AuthForm;
1 -import React from 'react';
2 -import styled from 'styled-components';
3 -/*
4 -register/login Layout
5 -*/
6 -
7 -const AuthTemplateBlock = styled.div``;
8 -
9 -const AuthTemplate = ({ children }) => {
10 - return <AuthTemplateBlock>{children}</AuthTemplateBlock>;
11 -};
12 -
13 -export default AuthTemplate;
1 +import React, { useEffect, useState } from 'react';
2 +import { useDispatch, useSelector } from 'react-redux';
3 +import { withRouter } from 'react-router-dom';
4 +import { changeField, initializeForm, login } from '../../modules/auth';
5 +import AuthForm from './AuthForm';
6 +
7 +const LoginForm = ({ history }) => {
8 + const dispatch = useDispatch();
9 + const [error, setError] = useState(null);
10 + const { form, auth, authError, user } = useSelector(({ auth, user }) => ({
11 + form: auth.login,
12 + auth: auth.auth,
13 + authError: auth.authError,
14 + user: user.user,
15 + }));
16 +
17 + const onChange = (e) => {
18 + const { value, name } = e.target;
19 + dispatch(
20 + changeField({
21 + form: 'login',
22 + key: name,
23 + value,
24 + }),
25 + );
26 + };
27 +
28 + const onSubmit = (e) => {
29 + e.preventDefault();
30 + const { username, password } = form;
31 + dispatch(login({ username, password }));
32 + };
33 +
34 + useEffect(() => {
35 + dispatch(initializeForm('login'));
36 + }, [dispatch]);
37 +
38 + useEffect(() => {
39 + if (authError) {
40 + console.log('Error Occured');
41 + console.log(authError);
42 + setError('로그인 실패');
43 + return;
44 + }
45 + if (auth) {
46 + console.log('Login Success');
47 + dispatch(check());
48 + }
49 + }, [auth, authError, dispatch]);
50 +
51 + useEffect(() => {
52 + if (user) {
53 + history.push('/');
54 + try {
55 + localStorage.setItem('user', JSON.stringify(user));
56 + } catch (e) {
57 + console.log('localStorage is not working');
58 + }
59 + console.log(user);
60 + }
61 + }, [history, user]);
62 +
63 + return <AuthForm type="login"></AuthForm>;
64 +};
65 +
66 +export default withRouter(LoginForm);
1 +import React, { useEffect, useState } from 'react';
2 +import { useDispatch, useSelector } from 'react-redux';
3 +import AuthForm from '../../components/auth/AuthForm';
4 +import { withRouter } from 'react-router-dom';
5 +
6 +const RegisterForm = ({ history }) => {
7 + const [error, setError] = useState(null);
8 + const dispatch = useDispatch();
9 + const { form, auth, authError, user } = useSelector(({ auth, user }) => ({
10 + form: auth.register,
11 + auth: auth.auth,
12 + authError: auth.authError,
13 + user: user.user,
14 + }));
15 +
16 + const onChange = (e) => {
17 + const { value, name } = e.target;
18 + dispatch(
19 + changeField({
20 + form: 'register',
21 + key: name,
22 + value,
23 + }),
24 + );
25 + };
26 +
27 + const onSubmit = (e) => {
28 + e.preventDefault();
29 + const { username, password, passwordConfirm } = form;
30 + if ([username, password, passwordConfirm].includes('')) {
31 + setError('빈 칸을 모두 입력하세요');
32 + return;
33 + }
34 + if (password !== passwordConfirm) {
35 + //Todo Handle Error
36 + setError('비밀번호가 일치하지 않습니다.');
37 + changeField({ form: 'register', key: 'password', value: '' });
38 + changeField({ form: 'register', key: 'passwordConfirm', value: '' });
39 + return;
40 + }
41 + dispatch(register({ username, password }));
42 + };
43 +
44 + useEffect(() => {
45 + dispatch(initializeForm('register'));
46 + }, [dispatch]);
47 + useEffect(() => {
48 + if (authError) {
49 + if (authError.response.status === 409) {
50 + setError('이미 존재하는 계정명입니다.');
51 + return;
52 + }
53 + setError('회원가입 실패');
54 + return;
55 + }
56 +
57 + if (auth) {
58 + console.log('Register Success!');
59 + console.log(auth);
60 + dispatch(check());
61 + }
62 + }, [auth, authError, dispatch]);
63 +
64 + useEffect(() => {
65 + if (user) {
66 + console.log('SUCCESS check API');
67 + history.push('/');
68 + try {
69 + localStorage.setItem('user', JSON.stringify(user));
70 + } catch (e) {
71 + console.log('localStorage is not working');
72 + }
73 + }
74 + }, [history, user]);
75 +
76 + return <AuthForm type="register" form={form}></AuthForm>;
77 +};
78 +
79 +export default withRouter(RegisterForm);
1 import { createAction, handleActions } from 'redux-actions'; 1 import { createAction, handleActions } from 'redux-actions';
2 +import produce from 'immer';
2 3
3 -const SAMPLE_ACTION = 'auth/SAMPLE_ACTION'; 4 +const CHANGE_FIELD = 'auth/CHANGE_FIELD';
5 +const INITIALIZE_FORM = 'auth/INITIALIZE_FORM';
4 6
5 export const sampleAction = createAction(SAMPLE_ACTION); 7 export const sampleAction = createAction(SAMPLE_ACTION);
8 +export const cahngeField = createAction(
9 + CHANGE_FIELD,
10 + ({ form, key, value }) => {
11 + form, key, value;
12 + },
13 +);
14 +export const initializeForm = createAction(INITIALIZE_FORM, (form) => form);
6 15
7 -const initialState = {}; 16 +const initialState = {
17 + register: {
18 + username: '',
19 + password: '',
20 + passwordConfirm: '',
21 + },
22 + login: {
23 + username: '',
24 + password: '',
25 + },
26 +};
8 27
9 const auth = handleActions( 28 const auth = handleActions(
10 { 29 {
11 - [SAMPLE_ACTION]: (state, action) => state, 30 + [CHANGE_FIELD]: (state, { payload: { form, key, value } }) =>
31 + produce(state, (draft) => {
32 + draft[form][key] = value;
33 + }),
34 + [INITIALIZE_FORM]: (state, { payload: form }) => ({
35 + ...state,
36 + [form]: initialState[form],
37 + }),
12 }, 38 },
13 initialState, 39 initialState,
14 ); 40 );
......
1 import React from 'react'; 1 import React from 'react';
2 import AuthTemplate from '../components/auth/AuthTemplate'; 2 import AuthTemplate from '../components/auth/AuthTemplate';
3 -import AuthForm from '../components/auth/AuthForm'; 3 +import LoginForm from '../containers/auth/LoginForm';
4 4
5 const LoginPage = () => { 5 const LoginPage = () => {
6 return ( 6 return (
7 <AuthTemplate> 7 <AuthTemplate>
8 - <AuthForm /> 8 + <LoginForm type="login" />
9 </AuthTemplate> 9 </AuthTemplate>
10 ); 10 );
11 }; 11 };
......
1 import React from 'react'; 1 import React from 'react';
2 +import AuthTemplate from '../components/auth/AuthTemplate';
3 +import RegisterForm from '../containers/auth/RegisterForm';
2 4
3 const RegisterPage = () => { 5 const RegisterPage = () => {
4 return ( 6 return (
5 <AuthTemplate> 7 <AuthTemplate>
6 - <AuthForm /> 8 + <RegisterForm type="register" />
7 </AuthTemplate> 9 </AuthTemplate>
8 ); 10 );
9 }; 11 };
......