Skip to content

Commit 1cd9321

Browse files
authored
Merge pull request #83 from codeit-sprint-part3-6team/#72_페이지_회원가입
#72 페이지 회원가입
2 parents bf766a2 + 71985aa commit 1cd9321

File tree

17 files changed

+536
-155
lines changed

17 files changed

+536
-155
lines changed
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
.check-field {
2+
margin-top: 16px;
3+
}
4+
5+
.text {
6+
font-size: 16px;
7+
font-weight: 400;
8+
color: var(--black-medium);
9+
cursor: pointer;
10+
}
11+
12+
/* checkbox 커스텀 */
13+
.check-field input[type='checkbox'] {
14+
position: absolute;
15+
width: 1px;
16+
height: 1px;
17+
padding: 0;
18+
margin: -1px;
19+
overflow: hidden;
20+
clip: rect(0, 0, 0, 0);
21+
border: 0;
22+
}
23+
24+
.check-field input[type='checkbox'] + label {
25+
display: inline-block;
26+
position: relative;
27+
padding-left: 28px;
28+
cursor: pointer;
29+
}
30+
31+
.check-field input[type='checkbox'] + label:before {
32+
content: '';
33+
border: 1px solid #d9d9d9;
34+
border-radius: 4px;
35+
background: #fff;
36+
width: 20px;
37+
height: 20px;
38+
text-align: center;
39+
position: absolute;
40+
left: 0;
41+
top: -1px;
42+
}
43+
44+
.check-field input[type='checkbox']:checked + label:after {
45+
content: '';
46+
border: 1px solid #d9d9d9;
47+
border-radius: 4px;
48+
background: #f1effd url('/ic/ic_check.svg') no-repeat 50%;
49+
background-size: 10px 8px;
50+
position: absolute;
51+
top: -1px;
52+
left: 0;
53+
width: 20px;
54+
height: 20px;
55+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import styles from './CheckBox.module.css';
2+
import { ChangeEvent } from 'react';
3+
4+
type CheckBoxProps = {
5+
name: string;
6+
id: string;
7+
htmlFor: string;
8+
text: string;
9+
onChange: (e: ChangeEvent<HTMLInputElement>) => void;
10+
};
11+
12+
function CheckBox({ name, id, htmlFor, text, onChange }: CheckBoxProps) {
13+
return (
14+
<div className={styles['check-field']}>
15+
<input
16+
name={name}
17+
id={id}
18+
type="checkbox"
19+
onChange={onChange}
20+
className={styles['input-checkbox']}
21+
/>
22+
<label htmlFor={htmlFor} className={styles.text}>
23+
{text}
24+
</label>
25+
</div>
26+
);
27+
}
28+
29+
export default CheckBox;

src/components/common/input/auth-input/AuthInput.module.css

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
border-radius: 8px;
2323
width: 100%;
2424
height: 50px;
25+
font-size: 16px;
26+
font-weight: 400;
2527
}
2628

2729
.auth-input input.password {
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
.login-button {
2+
margin-top: 24px;
3+
}
4+
5+
@media screen and (max-width: 743px) {
6+
.login-button {
7+
margin-top: 16px;
8+
}
9+
}
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
import styles from './SigninForm.module.css';
2+
import { ChangeEvent, FocusEvent, FormEvent, useEffect, useState } from 'react';
3+
import { useRouter } from 'next/router';
4+
import { useDispatch } from 'react-redux';
5+
import { setUserInfo } from '@/redux/settingSlice';
6+
import { AppDispatch } from '@/redux/store';
7+
import AuthInput from '@/components/common/input/auth-input/AuthInput';
8+
import CDSButton from '@/components/common/button/CDSButton';
9+
import OverlayContainer from '@/components/common/modal/overlay-container/OverlayContainer';
10+
import AuthModal from '@/components/common/modal/auth/AuthModal';
11+
import { postSignin } from '@/lib/signin/postSignin';
12+
import { ERROR_MESSAGE, PLACEHOLDER } from '@/constants/messages';
13+
import { emailValidation, passwordValidation } from '@/utils/authValidation';
14+
15+
const INITIAL_VALUES = {
16+
email: '',
17+
password: '',
18+
};
19+
20+
function SigninForm() {
21+
const [values, setValues] = useState(INITIAL_VALUES);
22+
const [emailValid, setEmailValid] = useState(false);
23+
const [passwordValid, setPasswordValid] = useState(false);
24+
const [disabled, setDisabled] = useState(true);
25+
const [isModalVisible, setIsModalVisible] = useState(false);
26+
const [responseMessage, setResponseMessage] = useState('');
27+
const router = useRouter();
28+
const dispatch = useDispatch<AppDispatch>();
29+
30+
useEffect(() => {
31+
const isEmailValid = emailValidation(values.email);
32+
const isPasswordValid = passwordValidation(values.password);
33+
setDisabled(!(isEmailValid && isPasswordValid));
34+
}, [values]);
35+
36+
useEffect(() => {
37+
if (sessionStorage.getItem('accessToken')) {
38+
router.push('/mydashboard');
39+
}
40+
}, []);
41+
42+
const handleChange = (e: ChangeEvent<HTMLInputElement>) => {
43+
const { name, value } = e.target;
44+
45+
setValues((prevValues) => ({
46+
...prevValues,
47+
[name]: value,
48+
}));
49+
};
50+
51+
const handleBlur = (e: FocusEvent<HTMLInputElement>) => {
52+
const { name, value } = e.target;
53+
54+
if (name === 'email') {
55+
setEmailValid(!emailValidation(value));
56+
} else if (name === 'password') {
57+
setPasswordValid(!passwordValidation(value));
58+
}
59+
};
60+
61+
const onSubmit = async (e: FormEvent<HTMLFormElement>) => {
62+
e.preventDefault();
63+
64+
try {
65+
const response = await postSignin(values);
66+
sessionStorage.setItem('accessToken', response.accessToken);
67+
68+
// 리덕스 액션 호출
69+
dispatch(
70+
setUserInfo({
71+
user: response.user,
72+
}),
73+
);
74+
75+
router.push('/mydashboard');
76+
} catch (error) {
77+
setResponseMessage(error.message);
78+
setIsModalVisible(true);
79+
}
80+
};
81+
82+
const handleCancelClick = () => {
83+
setIsModalVisible(false);
84+
setResponseMessage(null);
85+
};
86+
return (
87+
<>
88+
<form onSubmit={onSubmit}>
89+
<AuthInput
90+
name="email"
91+
htmlFor="email"
92+
title="이메일"
93+
id="email"
94+
type="text"
95+
placeholder={PLACEHOLDER.EMAIL_PLACEHOLDER}
96+
value={values.email}
97+
onChange={handleChange}
98+
onBlur={handleBlur}
99+
error={emailValid}
100+
errorMessage={ERROR_MESSAGE.EMAIL_INVALID_FORMAT}
101+
autoComplete="email"
102+
/>
103+
104+
<AuthInput
105+
name="password"
106+
htmlFor="password"
107+
title="비밀번호"
108+
id="password"
109+
type="password"
110+
placeholder="비밀번호를 입력해 주세요"
111+
value={values.password}
112+
onChange={handleChange}
113+
onBlur={handleBlur}
114+
error={passwordValid}
115+
errorMessage={ERROR_MESSAGE.PASSWORD_MIN_LENGTH}
116+
autoComplete="password"
117+
/>
118+
119+
<div className={styles['login-button']}>
120+
<CDSButton btnType="auth" type="submit" disabled={disabled}>
121+
로그인
122+
</CDSButton>
123+
</div>
124+
</form>
125+
126+
{isModalVisible && (
127+
<OverlayContainer>
128+
<AuthModal
129+
message={responseMessage}
130+
handleCancelClick={handleCancelClick}
131+
/>
132+
</OverlayContainer>
133+
)}
134+
</>
135+
);
136+
}
137+
138+
export default SigninForm;

0 commit comments

Comments
 (0)