Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import { BrowserRouter, Route, Routes } from "react-router-dom";
import HomePage from "./components/pages/Homepage.tsx";
import NavBar from "./components/nav/NavBar.tsx";
import GlobalStyle from "./components/style/GlobalStyle.tsx";
import LoginPage from "./components/pages/LoginPage.tsx";
import JoinPage from "./components/pages/JoinPage.tsx";

function App() {
return (
Expand All @@ -10,8 +12,9 @@ function App() {
<BrowserRouter>
<NavBar />
<Routes>
<Route path="/" element={<HomePage />} />

<Route path="/" element={<JoinPage />} />
<Route path="/home" element={<HomePage />} />
<Route path="/login" element={<LoginPage />} />
</Routes>
</BrowserRouter>
</div>
Expand Down
33 changes: 33 additions & 0 deletions src/components/join/Button.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import styled from "@emotion/styled";

const ButtonWrapStyle = styled.div`
cursor: pointer;
width: 100%;
background-color: rgb(0, 0, 128);
border-radius: 10px;
height: 50px;

border: 3px solid skyblue;

display: flex;
justify-content: center;
margin-top: 30px;
`;

const ButtonStyle = styled.button`
cursor: pointer;
color: white;
background-color: rgb(0, 0, 128);
font-weight: bold;
font-size: large;
border: none;
`;

const Button = () => {
return (
<ButtonWrapStyle>
<ButtonStyle>회원가입</ButtonStyle>
</ButtonWrapStyle>
);
};
export default Button;
9 changes: 9 additions & 0 deletions src/components/join/GotoLogin.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
const GotoLogin = () => {
return (
<div>
이미 계정이 있으신가요? <u>로그인</u>
</div>
);
};

export default GotoLogin;
21 changes: 21 additions & 0 deletions src/components/join/Input.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import InputItem from "./InputItem";
import styled from "@emotion/styled";

const InputWrapStyle = styled.div`
display: flex;
flex-direction: column;
gap: 10px;
`;

const Input = () => {
return (
<InputWrapStyle>
<InputItem content="별명" />
<InputItem content="아이디" />
<InputItem content="비밀번호" />
<InputItem content="비밀번호 확인" />
</InputWrapStyle>
);
};

export default Input;
22 changes: 22 additions & 0 deletions src/components/join/InputItem.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import styled from "@emotion/styled";

interface InputItemProps {
content: string;
}

const InputItemStyle = styled.input`
border-radius: 5px;
width: 100%;
height: 45px;
background-color: #e3e3e3;
border: none;
`;

const InputItem = ({ content }: InputItemProps) => {
return (
<div>
<InputItemStyle placeholder={content} />
</div>
);
};
export default InputItem;
26 changes: 26 additions & 0 deletions src/components/login/Button.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/** @jsxImportSource @emotion/react */
import styled from "@emotion/styled";
import { useNavigate } from "react-router-dom"; // navigate 훅 사용

const ButtonStyle = styled.button`
cursor: pointer;
color: white;
background-color: rgb(0, 0, 128);
font-weight: bold;
font-size: large;
border: none;
border-radius: 10px;
height: 50px;
width: 100%;
margin-top: 30px;
`;

const Button = () => {
const navigate = useNavigate();
const handleContinueLogin = () => {
navigate("/home"); // 로그인 후 이동할 경로
};
return <ButtonStyle onClick={handleContinueLogin}>로그인</ButtonStyle>;
};

export default Button;
74 changes: 74 additions & 0 deletions src/components/login/Input.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
// src/components/login/Input.tsx
/** @jsxImportSource @emotion/react */
import styled from "@emotion/styled";
import InputItem from "./InputItem";
import { useState } from "react";

interface InputProps {
id: string;
setId: React.Dispatch<React.SetStateAction<string>>;
pw: string;
setPw: React.Dispatch<React.SetStateAction<string>>;
handlePw?: (e: React.ChangeEvent<HTMLInputElement>) => void;
}

const InputStyle = styled.div`
display: flex;
flex-direction: column;
width: 100%;
`;

const InputWrapStyle = styled.div`
display: flex;
flex-direction: column;
gap: 20px;
`;

const ErrorMessageStyle = styled.div`
color: #ef0000;
font-size: 12px;
`;

const Input = ({ id, setId, pw, setPw }: InputProps) => {
const [pwValid, setPwValid] = useState<boolean>(false);

const handlePw = (e: React.ChangeEvent<HTMLInputElement>) => {
setPw(e.target.value);
const regex =
/^(?=.*[A-Za-z])(?=.*\d)(?=.*[!@#$%^&*])[A-Za-z\d!@#$%^&*]{8,}$/;
if (regex.test(pw)) {
setPwValid(true);
} else {
setPwValid(false);
}
};

return (
<InputStyle>
<InputWrapStyle>
<InputItem
content="아이디를 입력해주세요."
type="text"
value={id}
onChange={(e) => {
setId(e.target.value);
}}
/>
<InputItem
content="비밀번호를 입력해주세요."
type="password"
value={pw}
onChange={handlePw}
/>
</InputWrapStyle>

<ErrorMessageStyle>
{!pwValid && pw.length > 0 && (
<div>영문, 숫자, 특수문자 포함 8자 이상 입력해주세요.</div>
)}
</ErrorMessageStyle>
</InputStyle>
);
};

export default Input;
43 changes: 43 additions & 0 deletions src/components/login/InputItem.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// src/components/login/InputItem.tsx
/** @jsxImportSource @emotion/react */
import styled from "@emotion/styled";

interface InputItemProps {
content: string;
type: string;
value: string;
onChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
}

const InputStyle = styled.input`
border-radius: 5px;
width: 100%;
height: 40px;
background-color: #e3e3e3;
border: none;
outline: none;
font-size: 15px;
font-weight: 400;
`;

const InputItemStyle = styled.div`
display: flex;
border-radius: 8px;
background-color: #e3e3e3;
padding: 5px;
`;

const InputItem = ({ content, type, value, onChange }: InputItemProps) => {
return (
<InputItemStyle>
<InputStyle
placeholder={content}
type={type}
value={value}
onChange={onChange}
/>
</InputItemStyle>
);
};

export default InputItem;
48 changes: 48 additions & 0 deletions src/components/pages/JoinPage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import Input from "../join/Input";
import Button from "../join/Button";
import styled from "@emotion/styled";
import GotoLogin from "../join/GotoLogin";

const JoinContainerStyle = styled.div`
display: flex;
flex-direction: column;
gap: 20px;
width: 25%;
height: 60%;
justify-content: center;
align-items: center;

/* 페이지 전체 화면에서 중앙 정렬 */
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
h1 {
font-size: 40px;
}
`;

const JoinWperStyle = styled.div`
display: flex;
flex-direction: column;
gap: 10px;
justify-content: center;
width: 100%;
`;

const JoinPage = () => {
return (
<>
<JoinContainerStyle>
<h1>회원가입</h1>
<JoinWperStyle>
<Input />
<Button />
<GotoLogin />
</JoinWperStyle>
</JoinContainerStyle>
</>
);
};

export default JoinPage;
69 changes: 69 additions & 0 deletions src/components/pages/LoginPage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// src/components/login/Main.tsx
/** @jsxImportSource @emotion/react */
import styled from "@emotion/styled";
import { useState } from "react";
import Input from "../login/Input";
import Button from "../login/Button";

const WrapperStyle = styled.div`
display: flex;
flex-direction: column;
gap: 10px;
justify-content: center;
align-items: center;
width: 100%;
`;
const MainContainerStyle = styled.div`
display: flex;
flex-direction: column;
gap: 20px;
width: 25%;
height: 60%;
justify-content: center;
align-items: center;

/* 페이지 전체 화면에서 중앙 정렬 */
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
h1 {
font-size: 40px;
}

p {
cursor: pointer;
justify-content: center;
width: auto;
}
`;
const LoginPage = () => {
const [id, setId] = useState<string>("");
const [pw, setPw] = useState<string>("");
// const handlePw = (e: React.ChangeEvent<HTMLInputElement>) => {
// setPw(e.target.value);
// };
console.log("로그인 페이지 렌더링");
return (
<>
<MainContainerStyle>
<h1>로그인</h1>
<WrapperStyle>
<Input
id={id}
setId={setId}
pw={pw}
setPw={setPw}
// handlePw={handlePw}
/>
<Button />
</WrapperStyle>
<div>
<p> 회원가입 </p>
</div>
</MainContainerStyle>
</>
);
};

export default LoginPage;
Loading