Skip to content

Commit

Permalink
[FE] 마일스톤 추가, 수정, 삭제 기능 및 기타 스타일 수정 (#136)
Browse files Browse the repository at this point in the history
* #133 refactor: 로그아웃 버튼, 로그인 라우팅 수정, textarea 줄바꿈 css 속성 추가

* #125 feat: 마일스톤 추가 기능

* #125 feat: 마일스톤 수정 & 삭제 기능

* #133 refactor: 타입, 스타일 수정

- 마일스톤 없을 때도 빈 아이템 보여주도록 수정
- openIssueCount, closedIssueCount number 타입으로 수정

* refactor: 리뷰 반영
  • Loading branch information
youzysu committed Aug 11, 2023
1 parent 6990cce commit e43b3eb
Show file tree
Hide file tree
Showing 11 changed files with 453 additions and 45 deletions.
32 changes: 32 additions & 0 deletions fe/src/api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -143,3 +143,35 @@ export const postComment = async (
) => {
return await fetcherWithBearer.post(`/issues/${issueId}/comments`, body);
};

export const postMilestone = async (body: {
milestoneName: string;
dueDate: string;
description: string;
}) => {
return await fetcherWithBearer.post("/milestones", body);
};

export const putMilestoneContent = async (
milestoneId: number,
body: {
milestoneName: string;
dueDate: string;
description: string;
}
) => {
return await fetcherWithBearer.put(`/milestones/${milestoneId}`, body);
};

export const putMilestoneState = async (
milestoneId: number,
state: "open" | "closed"
) => {
return await fetcherWithBearer.put(
`/milestones/${milestoneId}?state=${state}`
);
};

export const deleteMilestone = async (milestoneId: number) => {
return await fetcherWithBearer.delete(`/milestones/${milestoneId}`);
};
186 changes: 186 additions & 0 deletions fe/src/components/Milestone/MilestoneEditor.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
import plusIcon from "@assets/icon/plus.svg";
import xIcon from "@assets/icon/xSquare.svg";
import Button from "@components/common/Button";
import TextInput from "@components/common/TextInput";
import { validateDate } from "@utils/time";
import { postMilestone, putMilestoneContent } from "api";
import { ChangeEvent, FormEvent, useState } from "react";
import { useNavigate } from "react-router-dom";
import styled from "styled-components";

export type MilestoneInfo = {
milestoneName: string;
dueDate?: string;
description?: string;
};

export default function MilestoneEditor({
variant,
milestoneId,
milestoneInfo,
closeEditor,
}: {
variant: "add" | "edit";
milestoneId?: number;
milestoneInfo?: MilestoneInfo | null;
closeEditor: () => void;
}) {
const navigate = useNavigate();

// TODO: dueDate useInput validate 개선 후 적용
const [newMilestone, setNewMilestone] = useState({
milestoneName: milestoneInfo?.milestoneName || "",
dueDate: milestoneInfo?.dueDate || "",
description: milestoneInfo?.description || "",
});

const isValidDueDate = validateDate(newMilestone.dueDate);

const onNameChange = (e: ChangeEvent<HTMLInputElement>) => {
setNewMilestone((prev) => {
return { ...prev, milestoneName: e.target.value };
});
};

const onDueDateChange = (e: ChangeEvent<HTMLInputElement>) => {
setNewMilestone((prev) => {
return { ...prev, dueDate: e.target.value };
});
};

const onDescriptionChange = (e: ChangeEvent<HTMLInputElement>) => {
setNewMilestone((prev) => {
return { ...prev, description: e.target.value };
});
};

// TODO: 개선 필요
const isReadyToSubmit = {
add: !!newMilestone.milestoneName,
edit:
!!newMilestone.milestoneName &&
(newMilestone.milestoneName !== milestoneInfo?.milestoneName ||
newMilestone.dueDate !== milestoneInfo?.dueDate ||
newMilestone.description !== milestoneInfo?.description),
};

const onSubmit = async (e: FormEvent) => {
e.preventDefault();

// TODO: 수정된 내용이 있는 영역만 보내기
try {
const { milestoneName, dueDate, description } = newMilestone;
const body = {
milestoneName,
description,
dueDate,
};

const res = milestoneId
? await putMilestoneContent(milestoneId, body)
: await postMilestone(body);

if (res.status === 201 || res.status === 200) {
// TODO: 마일스톤 상태 관리 필요
navigate(0);
}
} catch (error) {
// TODO: error handling
console.log(error);
}
};

return (
<StyledMilestoneEditor>
<H2>{variant === "add" ? "새로운 마일스톤 추가" : "마일스톤 편집"}</H2>

<EditForm onSubmit={onSubmit}>
<InputsWrapper>
<div className="upper-wrapper">
<TextInput
name="제목"
variant="short"
value={newMilestone.milestoneName}
placeholder="마일스톤의 이름을 입력하세요"
onChange={onNameChange}
/>
<TextInput
name="완료일(선택)"
variant="short"
value={newMilestone.dueDate}
placeholder="YYYY-MM-DD"
onChange={onDueDateChange}
hasError={!!newMilestone.dueDate && !isValidDueDate}
helpText='"YYYY-MM-DD" 형식만 가능해요.'
/>
</div>
<TextInput
name="설명(선택)"
variant="short"
value={newMilestone.description}
placeholder="마일스톤에 대한 설명을 입력하세요"
onChange={onDescriptionChange}
/>
</InputsWrapper>
<ButtonsWrapper>
<Button
type="button"
variant="outline"
size="S"
onClick={closeEditor}>
<img src={xIcon} alt="취소" />
<span>취소</span>
</Button>
<Button
type="submit"
variant="container"
size="S"
disabled={!isReadyToSubmit[variant]}>
<img src={plusIcon} alt="완료" />
<span>완료</span>
</Button>
</ButtonsWrapper>
</EditForm>
</StyledMilestoneEditor>
);
}

const StyledMilestoneEditor = styled.div`
width: 100%;
`;

const EditForm = styled.form`
width: 100%;
display: flex;
flex-direction: column;
gap: 24px;
.inner-wrapper {
display: flex;
gap: 24px;
}
`;

const H2 = styled.h2`
margin-bottom: 24px;
font: ${({ theme: { font } }) => font.displayBold20};
color: ${({ theme: { neutral } }) => neutral.text.strong};
`;

const ButtonsWrapper = styled.div`
display: flex;
gap: 16px;
justify-content: flex-end;
`;

const InputsWrapper = styled.div`
display: flex;
width: 100%;
flex-direction: column;
gap: 1rem;
.upper-wrapper {
display: flex;
gap: 1rem;
}
`;
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@ export default function MilestonesTableBody({
}: {
milestoneList: Milestone[] | null;
}) {
const hasMilestone = milestoneList && milestoneList.length !== 0;

return (
<TableBody>
{milestoneList ? (
{hasMilestone ? (
<ul>
{milestoneList.map((milestone) => (
<MilestonesTableItem
Expand All @@ -19,7 +21,7 @@ export default function MilestonesTableBody({
))}
</ul>
) : (
<EmptyTableBodyItem>등록된 마일스톤이 없습니다.</EmptyTableBodyItem>
<EmptyTableBodyItem>마일스톤이 없습니다.</EmptyTableBodyItem>
)}
</TableBody>
);
Expand Down
Loading

0 comments on commit e43b3eb

Please sign in to comment.