Skip to content

Commit

Permalink
refactor/BibimMandu-IssueTacker#215: 페이지 컴포넌트 리팩토링
Browse files Browse the repository at this point in the history
  • Loading branch information
qkdflrgs committed Aug 18, 2023
1 parent f2539ce commit 376b01d
Show file tree
Hide file tree
Showing 2 changed files with 215 additions and 66 deletions.
177 changes: 125 additions & 52 deletions FE/src/pages/AddIssuePage.tsx
Original file line number Diff line number Diff line change
@@ -1,25 +1,72 @@
import { styled } from "styled-components";
import UserProfileButton from "../components/UserProfileButton/UserProfileButton";
import { useNavigate } from "react-router-dom";
import TextArea from "../components/common/TextArea/TextArea";
import { useState } from "react";
import LabelInput from "../components/common/TextInput/LabelInput";
import SideBar from "../components/SideBar/SideBar";
// import SideBar from "../components/SideBar/SideBar";
import Button from "../components/common/Button/Button";
import SideBar from "../components/SideBar/SideBar";
import { AssigneesList, Label, Milestone } from "../type";
import Header from "../components/Header/Header";
import { useAuth } from "../context/AuthContext";

type Props = {
toggleTheme(): void;
};

export default function AddIssuePage() {
export default function AddIssuePage({ toggleTheme }: Props) {
const { profile } = useAuth();
const [issueTitle, setIssueTitle] = useState<string>("");
const [issueContent, setIssueContent] = useState<string>("");
const [assignees, setAssignees] = useState([1]);
const [labels, setLabels] = useState([4]);
const [milestone, setMilestone] = useState(null);
const [selectedAssignees, setSelectedAssignees] = useState<AssigneesList[]>(
[],
);
const [selectedLabels, setSelectedLabels] = useState<Label[]>([]);
const [selectedMilestone, setSelectedMilestone] = useState<Milestone>();
const navigate = useNavigate();

const handleAssignee = (assignee: AssigneesList, assigneeListId: number) => {
if (
selectedAssignees.length === 0 ||
selectedAssignees.some((assignee) => assignee.id !== assigneeListId)
) {
setSelectedAssignees((prevList) => [...prevList, assignee]);
}
if (selectedAssignees.some((assignee) => assignee.id === assigneeListId)) {
setSelectedAssignees(
selectedAssignees.filter((assignee) => assignee.id !== assigneeListId),
);
}
};

const handleLabel = (label: Label, labelListId: number) => {
if (
selectedLabels.length === 0 ||
selectedLabels.some((label) => label.id !== labelListId)
) {
setSelectedLabels((prevList) => [...prevList, label]);
}
if (selectedLabels.some((assignee) => assignee.id === labelListId)) {
setSelectedLabels(
selectedLabels.filter((label) => label.id !== labelListId),
);
}
};

const handleMilestone = (milestone: Milestone, milestoneListId: number) => {
if (
selectedMilestone === undefined ||
selectedMilestone!.id !== milestoneListId
)
setSelectedMilestone(milestone);
if (selectedMilestone!.id === milestoneListId)
setSelectedMilestone(undefined);
};

const goMainPage = () => {
navigate("/issues/isOpen=true");
};
setAssignees;
setLabels;
setMilestone;

const handleTitleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setIssueTitle(e.target.value);
};
Expand All @@ -33,16 +80,23 @@ export default function AddIssuePage() {
const createIssue = async () => {
const url = "http://3.34.141.196/api/issues";

const headers = new Headers();
const accessToken = localStorage.getItem("accessToken");
headers.append("Authorization", `Bearer ${accessToken}`);
headers.append("Content-Type", "application/json");

const data = {
title: issueTitle,
content: issueContent,
assigneeIds: assignees,
labelIds: labels,
milestoneId: milestone,
};

const headers = {
"Content-Type": "application/json",
assigneeIds:
selectedAssignees.length === 0
? null
: selectedAssignees.map((assignee) => assignee.id),
labelIds:
selectedLabels.length === 0
? null
: selectedLabels.map((label) => label.id),
milestoneId: selectedMilestone ? selectedMilestone.id : null,
};

try {
Expand All @@ -64,44 +118,57 @@ export default function AddIssuePage() {
};

return (
<Page>
<Main>
<Title>새로운 이슈 작성</Title>
<ContentsContainer>
<UserProfileButton src={"/logo/profile.jpg"} onClick={() => {}} />
<AddIssueForm>
<LabelInput
id={"newTitle"}
label={"제목"}
placeholder={"제목을 입력해주세요"}
value={issueTitle}
onChange={handleTitleInputChange}
<>
<Header
toggleTheme={toggleTheme}
profileImg={profile ? profile.profileImageUri : "/icons/user.png"}
/>
<Page>
<Main>
<Title>새로운 이슈 작성</Title>
<ContentsContainer>
<UserProfileImg src={"/logo/profile.jpg"} />
<AddIssueForm>
<LabelInput
id={"newTitle"}
label={"제목"}
placeholder={"제목을 입력해주세요"}
value={issueTitle}
onChange={handleTitleInputChange}
/>
<TextArea
inputValue={issueContent}
onChange={handleContentInputChange}
/>
</AddIssueForm>
<SideBar
assignees={selectedAssignees}
labels={selectedLabels}
milestone={selectedMilestone}
handleAssignee={handleAssignee}
handleLabel={handleLabel}
handleMilestone={handleMilestone}
/>
</ContentsContainer>
<ButtonTap>
<Button
icon={"xSquare"}
label={"작성 취소"}
type={"ghost"}
size={"medium"}
onClick={goMainPage}
/>
<TextArea
inputValue={issueContent}
onChange={handleContentInputChange}
<Button
label={"완료"}
type={"container"}
size={"large"}
onClick={createIssue}
disabled={!issueTitle}
/>
</AddIssueForm>
<SideBar></SideBar>
</ContentsContainer>
<ButtonTap>
<Button
icon={"xSquare"}
label={"작성 취소"}
type={"ghost"}
size={"medium"}
onClick={goMainPage}
/>
<Button
label={"완료"}
type={"container"}
size={"large"}
onClick={createIssue}
disabled={!issueTitle}
/>
</ButtonTap>
</Main>
</Page>
</ButtonTap>
</Main>
</Page>
</>
);
}

Expand Down Expand Up @@ -168,3 +235,9 @@ const ButtonTap = styled.div`
gap: 32px;
justify-content: end;
`;

const UserProfileImg = styled.img`
width: 32px;
height: 32px;
border-radius: ${({ theme }) => theme.radius.half};
`;
104 changes: 90 additions & 14 deletions FE/src/pages/LoginPage.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,40 @@
import { styled } from "styled-components";
import { keyframes, styled } from "styled-components";
import Button from "../components/common/Button/Button";
import TextInput from "../components/common/TextInput/TextInput";
import { useEffect, useState } from "react";
import { useNavigate } from "react-router-dom";
import { isValidEmail } from "../utils/isValidEmail";
import Alert from "../components/Alert/Alert";
import { useAuth } from "../context/AuthContext";
import { decodeBase64 } from "../utils/decodeBase64";

export default function LoginPage() {
const [hasIdError, setHasIdError] = useState<boolean>(false);
const [hasPasswordError, setHasPasswordError] = useState<boolean>(false);
const [idInputValue, setIdInputValue] = useState<string>("");
const [passwordInputValue, setPasswordInputValue] = useState<string>("");
const [loginError, setLoginError] = useState<boolean>(false);
const [loginErrorMessage, setLoginErrorMessage] = useState<string>();

const { updateProfile } = useAuth();
const navigate = useNavigate();
const goMainPage = () => {
navigate("/issues/isOpen=true");
};

const goGoogle = () => {
window.location.href = "https://www.google.com";
const goLoginPage = () => {
setLoginError(false);
navigate("/");
};

const goJoinPage = () => {
navigate("/join");
};

const goGitHubLogin = () => {
window.location.assign(
"https://github.com/login/oauth/authorize?client_id=Iv1.7ee4a0c3b7d0f238&redirect_uri=http://localhost:5173/redirect/oauth",
);
};

const handleIdInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
Expand All @@ -29,11 +47,47 @@ export default function LoginPage() {
setPasswordInputValue(e.target.value);
};

const onLogin = async () => {
const URL = `http://3.34.141.196/api/account/login`;

const postData = {
email: idInputValue,
password: passwordInputValue,
};

const headers = {
"Content-Type": "application/json",
};

try {
const response = await fetch(URL, {
method: "POST",
headers: headers,
body: JSON.stringify(postData),
});

if (response.status === 200) {
const data = await response.json();
localStorage.setItem("accessToken", data.accessToken);
localStorage.setItem("refreshToken", data.refreshToken);
updateProfile(decodeBase64(data.accessToken));
goMainPage();
} else {
setLoginErrorMessage("아이디, 혹은 비밀번호가 일치하지 않습니다");
setLoginError(true);
}
} catch (error) {
console.error("API 호출 오류:", error);
}
};

useEffect(() => {
(idInputValue.length > 0 && idInputValue.length < 6) ||
idInputValue.length > 12
? setHasIdError(true)
: setHasIdError(false);
if (idInputValue === "") {
setHasIdError(false);
return;
}

setHasIdError(!isValidEmail(idInputValue));
}, [idInputValue]);

useEffect(() => {
Expand All @@ -54,17 +108,17 @@ export default function LoginPage() {
type={"outline"}
size={"large"}
width={"100%"}
onClick={goGoogle}
onClick={goGitHubLogin}
/>
<Span>or</Span>
<Form>
<Id>
<TextInput
id={"id"}
label={"아이디"}
label={"이메일"}
hasCaption={hasIdError}
caption={"영문, 숫자 조합 6자 이상 12자 이하"}
captionType={"normal"}
caption={"이메일 형식으로 입력해주세요"}
captionType={"error"}
onChange={handleIdInputChange}
/>
</Id>
Expand All @@ -84,7 +138,7 @@ export default function LoginPage() {
label={"아이디로 로그인"}
size={"large"}
width={"100%"}
onClick={goMainPage}
onClick={onLogin}
disabled={
!idInputValue ||
!passwordInputValue ||
Expand All @@ -93,16 +147,37 @@ export default function LoginPage() {
}
/>
<Button
label={"회원가입"}
label={"계정 만들기"}
type={"ghost"}
size={"medium"}
onClick={() => {}}
onClick={goJoinPage}
/>
</Container>
{loginError && (
<Alert
content={loginErrorMessage!}
leftButtonLabel={"다시 로그인하기"}
rightButtonLabel={"회원가입 하기"}
onClickLeftButton={goLoginPage}
onClickRightButton={goJoinPage}
/>
)}
</Main>
);
}

const slideInRight = keyframes`
from {
transform: translateX(-50%);
opacity: 0;
}
to {
transform: translateX(0%);
opacity: 1;
}
`;

const Main = styled.div`
width: 100vw;
height: 100vh;
Expand Down Expand Up @@ -130,6 +205,7 @@ const Container = styled.div`
display: flex;
flex-direction: column;
gap: 16px;
animation: ${slideInRight} 0.5s ease-in-out;
`;

const Span = styled.span`
Expand Down

0 comments on commit 376b01d

Please sign in to comment.