|
1 | | -// App.tsx |
2 | | -import React from 'react'; |
3 | | -import LoginIllustration from '../../assets/login-illustration.svg'; |
4 | | -import BookVector from '../../assets/book_vector.svg' |
5 | | -import { useNavigate } from 'react-router-dom'; |
| 1 | +import { useState } from "react"; |
| 2 | +import LoginIllustration from "../../assets/login-illustration.svg"; |
| 3 | +import logo from "../../assets/image/logo.svg"; |
| 4 | +import { useNavigate } from "react-router-dom"; |
| 5 | +import signIn from "@/hooks/usrLogin"; |
6 | 6 |
|
7 | 7 | // Styled Components |
8 | 8 | import { |
9 | | - AppContainer, Header, HeaderContent, BookLogo, HeaderLogo, |
10 | | - Main, ContentContainer, ImageSection, FormSection, |
11 | | - WelcomeText, Form, InputGroup, Input, PasswordInput, |
12 | | - EyeIcon, RememberSection, CheckboxLabel, Checkbox, |
13 | | - ForgotPassword, LoginButton, RegisterSection, RegisterLink |
14 | | -} from './LoginPage.style'; |
| 9 | + AppContainer, |
| 10 | + Header, |
| 11 | + Main, |
| 12 | + ContentContainer, |
| 13 | + ImageSection, |
| 14 | + FormSection, |
| 15 | + WelcomeText, |
| 16 | + Form, |
| 17 | + InputGroup, |
| 18 | + Input, |
| 19 | + PasswordInput, |
| 20 | + EyeIcon, |
| 21 | + RememberSection, |
| 22 | + CheckboxLabel, |
| 23 | + Checkbox, |
| 24 | + ForgotPassword, |
| 25 | + LoginButton, |
| 26 | + RegisterSection, |
| 27 | + RegisterLink, |
| 28 | +} from "./LoginPage.style"; |
15 | 29 |
|
16 | 30 | const LoginPage: React.FC = () => { |
17 | 31 | let navigate = useNavigate(); |
18 | 32 |
|
| 33 | + const [formData, setFormData] = useState({ |
| 34 | + email: "", |
| 35 | + password: "", |
| 36 | + }); |
| 37 | + const [error, setError] = useState<string>(""); |
| 38 | + |
| 39 | + // 이메일, 비밀번호 입력값 상태 업데이트 함수 |
| 40 | + const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => { |
| 41 | + const { name, value } = e.target; |
| 42 | + setFormData((prev) => ({ |
| 43 | + ...prev, |
| 44 | + [name]: value, |
| 45 | + })); |
| 46 | + }; |
| 47 | + |
| 48 | + const handleLogin = async (e: React.FormEvent) => { |
| 49 | + e.preventDefault(); // 폼 제출 기본 동작 방지 |
| 50 | + |
| 51 | + if (!formData.email || !formData.password) { |
| 52 | + setError("이메일과 비밀번호를 모두 입력해주세요."); |
| 53 | + return; |
| 54 | + } |
| 55 | + |
| 56 | + try { |
| 57 | + // 로그인 API 호출 |
| 58 | + await signIn(formData.email, formData.password); |
| 59 | + |
| 60 | + if (localStorage.getItem("access_token")) { |
| 61 | + // 로그인 성공 시, 대시보드로 이동 |
| 62 | + navigate("/main"); |
| 63 | + } else { |
| 64 | + setError("로그인 실패. 이메일 또는 비밀번호를 확인해주세요."); |
| 65 | + } |
| 66 | + } catch (err: any) { |
| 67 | + console.error("로그인 중 오류 발생:", err); |
| 68 | + setError("로그인 중 오류가 발생했습니다. 다시 시도해주세요."); |
| 69 | + } |
| 70 | + }; |
| 71 | + |
19 | 72 | return ( |
20 | 73 | <AppContainer> |
21 | 74 | <Header> |
22 | | - <HeaderContent> |
23 | | - <BookLogo src={BookVector} alt="Book Logo" /> |
24 | | - <HeaderLogo>XRPedia</HeaderLogo> |
25 | | - </HeaderContent> |
| 75 | + <img src={logo} alt="Book Logo" /> |
26 | 76 | </Header> |
27 | | - |
| 77 | + |
28 | 78 | <Main> |
29 | 79 | <ContentContainer> |
30 | 80 | <ImageSection> |
31 | | - <img |
32 | | - src={LoginIllustration} |
33 | | - alt="Woman interacting with documents" |
34 | | - style={{ maxWidth: '100%', height: 'auto' }} |
| 81 | + <img |
| 82 | + src={LoginIllustration} |
| 83 | + alt="Woman interacting with documents" |
| 84 | + style={{ maxWidth: "100%", height: "auto" }} |
35 | 85 | /> |
36 | 86 | </ImageSection> |
37 | | - |
| 87 | + |
38 | 88 | <FormSection> |
39 | | - <WelcomeText> |
40 | | - XRPedia에 온 것을 환영해요! 👋 |
41 | | - </WelcomeText> |
42 | | - |
43 | | - <Form> |
| 89 | + <WelcomeText>XRPedia에 온 것을 환영해요! 👋</WelcomeText> |
| 90 | + |
| 91 | + <Form onSubmit={handleLogin}> |
| 92 | + {error && <p style={{ color: "red" }}>{error}</p>} |
44 | 93 | <InputGroup> |
45 | | - <Input type="email" placeholder="e-mail" /> |
| 94 | + <Input |
| 95 | + type="email" |
| 96 | + name="email" |
| 97 | + placeholder="e-mail" |
| 98 | + value={formData.email} |
| 99 | + onChange={handleInputChange} |
| 100 | + /> |
46 | 101 | </InputGroup> |
47 | | - |
| 102 | + |
48 | 103 | <PasswordInput> |
49 | | - <Input type="password" placeholder="password" /> |
| 104 | + <Input |
| 105 | + type="password" |
| 106 | + name="password" |
| 107 | + placeholder="password" |
| 108 | + value={formData.password} |
| 109 | + onChange={handleInputChange} |
| 110 | + /> |
50 | 111 | <EyeIcon> |
51 | | - <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"> |
| 112 | + <svg |
| 113 | + width="16" |
| 114 | + height="16" |
| 115 | + viewBox="0 0 24 24" |
| 116 | + fill="none" |
| 117 | + stroke="currentColor" |
| 118 | + strokeWidth="2" |
| 119 | + > |
52 | 120 | <path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"></path> |
53 | 121 | <circle cx="12" cy="12" r="3"></circle> |
54 | 122 | </svg> |
55 | 123 | </EyeIcon> |
56 | 124 | </PasswordInput> |
57 | | - |
| 125 | + |
58 | 126 | <RememberSection> |
59 | 127 | <CheckboxLabel> |
60 | 128 | <Checkbox type="checkbox" /> |
61 | 129 | 로그인 상태 유지 |
62 | 130 | </CheckboxLabel> |
63 | | - |
| 131 | + |
64 | 132 | <ForgotPassword href="#">비밀번호 찾기</ForgotPassword> |
65 | 133 | </RememberSection> |
66 | | - |
| 134 | + |
67 | 135 | <LoginButton type="submit">로그인</LoginButton> |
68 | 136 | </Form> |
69 | | - |
| 137 | + |
70 | 138 | <RegisterSection> |
71 | 139 | <span>계정이 없으신가요?</span> |
72 | | - <RegisterLink onClick={() => navigate('/signup')}>회원가입</RegisterLink> |
| 140 | + <RegisterLink onClick={() => navigate("/signup")}> |
| 141 | + 회원가입 |
| 142 | + </RegisterLink> |
73 | 143 | </RegisterSection> |
74 | 144 | </FormSection> |
75 | 145 | </ContentContainer> |
|
0 commit comments