Skip to content

Commit

Permalink
[FE] refactor/#158: 이슈 목록 페이지 필터 기능 리팩토링 (#159)
Browse files Browse the repository at this point in the history
* refactor/#158: 메인 페이지 필터 드롭다운 인디케이터 수정

* refactor/#158: 필터 타입 추가

* refactor/#158: 인풋 컴포넌트 수정

* refactor/#158: 라벨 인풋 컴포넌트 스타일 수정

* refactor/#158: 드롭다운 인디케이터 컴포넌트 리팩토링

* refactor/#158: 드롭다운 아이템 컴포넌트 리팩토링

* refactor/#158: 드롭다운 패널 컴포넌트 리팩토링

* refactor/#158: 필터바 컴포넌트 내 드롭다운 수정

* refactor/#158: 레이블 리스트 컴포넌트 수정

* refactor/#158: 사이드바 컴포넌트 내부 인디케이터 수정

* refactor/#158: 레이블 상세 페이지 수정
  • Loading branch information
qkdflrgs authored Aug 11, 2023
1 parent 172d259 commit fc5b45e
Show file tree
Hide file tree
Showing 11 changed files with 289 additions and 47 deletions.
75 changes: 70 additions & 5 deletions FE/src/components/DropdownIndicator/DropdownIndicator.tsx
Original file line number Diff line number Diff line change
@@ -1,25 +1,89 @@
import { useState } from "react";
import { styled } from "styled-components";
import DropdownPanel from "../DropdownPanel/DropdownPanel";

type Props = {
type?: "filter" | "assignees" | "labels" | "milestones" | "authors" | "none";
icon?: string;
label: string;
padding?: string;
width?: string;
height?: string;
dropdownTop?: string;
dropdownLeft?: string;
};

export default function DropdownIndicator({
type = "filter",
icon = "chevronDown",
label,
padding = "4px 0px",
width = "80px",
height = "32px",
dropdownTop = "0px",
dropdownLeft = "0px",
}: Props) {
const [openDropdown, setOpenDropdown] = useState<boolean>(false);

const openDropdownPanel = () => {
setOpenDropdown(true);
};

const closeDropdownPanel = () => {
setOpenDropdown(false);
};

return (
<IndicatorButton $padding={padding} $width={width} $height={height}>
<IndicatorLabel>{label}</IndicatorLabel>
<IndicatorIcon src={`/icons/${icon}.svg`} />
</IndicatorButton>
<Container>
<IndicatorButton
$padding={padding}
$width={width}
$height={height}
onClick={openDropdownPanel}
>
<IndicatorLabel>{label}</IndicatorLabel>
<IndicatorIcon src={`/icons/${icon}.svg`} />
</IndicatorButton>
{openDropdown && type === "filter" && (
<DropdownPanel
top={dropdownTop}
left={dropdownLeft}
closeDropdown={closeDropdownPanel}
/>
)}
{openDropdown && type === "assignees" && (
<DropdownPanel
type={"assignees"}
top={"40px"}
left={"0px"}
closeDropdown={closeDropdownPanel}
/>
)}
{openDropdown && type === "labels" && (
<DropdownPanel
type={"labels"}
top={"40px"}
left={"112px"}
closeDropdown={closeDropdownPanel}
/>
)}
{openDropdown && type === "milestones" && (
<DropdownPanel
type={"milestones"}
top={"40px"}
left={"180px"}
closeDropdown={closeDropdownPanel}
/>
)}
{openDropdown && type === "authors" && (
<DropdownPanel
type={"authors"}
top={"40px"}
left={"200px"}
closeDropdown={closeDropdownPanel}
/>
)}
</Container>
);
}

Expand All @@ -28,7 +92,6 @@ const IndicatorButton = styled.button<{
$width: string;
$height: string;
}>`
position: relative;
width: ${({ $width }) => $width};
height: ${({ $height }) => $height};
display: flex;
Expand All @@ -46,6 +109,8 @@ const IndicatorButton = styled.button<{
}
`;

const Container = styled.div``;

const IndicatorLabel = styled.span`
font: ${({ theme }) => theme.font.availableMedium16};
color: ${({ theme }) => theme.colorSystem.neutral.text.default};
Expand Down
14 changes: 5 additions & 9 deletions FE/src/components/DropdownPanel/DropdownItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,38 +2,34 @@ import { styled } from "styled-components";
import UserProfileButton from "../UserProfileButton/UserProfileButton";

type Props = {
id: number;
userImg?: string;
itemName: string;
onClick(): void;
};

export default function DropdownItem({
id,
userImg = "/logo/profile.jpg",
userImg = "",
itemName,
onClick,
}: Props) {
return (
<Container onClick={onClick}>
<Info>
{userImg && <UserProfileButton src={userImg} size={"small"} />}
<Label>
{itemName}
{id}
</Label>
<Label>{itemName}</Label>
</Info>
<Checkbox type={"checkbox"}></Checkbox>
</Container>
);
}

const Container = styled.div`
const Container = styled.button`
padding: 10px 16px;
width: 100%;
z-index: 1000;
display: flex;
justify-content: space-between;
align-items: center;
background-color: ${({ theme }) => theme.colorSystem.neutral.surface.strong};
border-top: ${({ theme }) =>
`${theme.border.default} ${theme.colorSystem.neutral.border.default}`};
`;
Expand Down
199 changes: 184 additions & 15 deletions FE/src/components/DropdownPanel/DropdownPanel.tsx
Original file line number Diff line number Diff line change
@@ -1,43 +1,212 @@
import { styled } from "styled-components";
import { AssigneesProps } from "../../type";
import { useEffect, useRef, useState } from "react";
import DropdownItem from "./DropdownItem";
import { AssigneesList, DropdownMilestone, Label } from "../../type";
import { useNavigate } from "react-router-dom";

type Props = {
title: string;
assigneesList: AssigneesProps;
type?: "filter" | "assignees" | "labels" | "milestones" | "authors";
top: string;
left: string;
closeDropdown(): void;
};

type AssigneesData = {
assignees: AssigneesList[] | null;
};

type LabelsData = {
labels: Label[] | null;
};

type MilestonesData = {
milestones: DropdownMilestone[] | null;
};

type AuthorsData = {
authors: AssigneesList[] | null;
};

export default function DropdownPanel({
title,
assigneesList,
type = "filter",
top,
left,
closeDropdown,
}: Props) {
const navigate = useNavigate();
const dropdownRef = useRef<HTMLDivElement>(null);
const [assigneesData, setAssigneesData] = useState<AssigneesData | null>(
null,
);
const [labelsData, setLabelsData] = useState<LabelsData | null>(null);
const [milestonesData, setMilestonesData] = useState<MilestonesData | null>(
null,
);
const [authorsData, setAuthorsData] = useState<AuthorsData | null>(null);

const showOpenIssue = () => {
closeDropdown();
navigate("/issues/isOpen=true");
};

const showCloseIssue = () => {
closeDropdown();
navigate("/issues/isOpen=false");
};

const showAuthoredByMe = () => {
closeDropdown();
navigate("/issues/authorId=1");
};

const showAssignedIssue = () => {
closeDropdown();
navigate("/issues/assigneeId=1");
};

const showCommentedIssue = () => {
closeDropdown();
navigate("/issues/isCommentedByMe=true");
};

useEffect(() => {
if (type === "filter") {
return;
}
const URL = `http://3.34.141.196/api/issues/${type}`;
const fetchData = async () => {
try {
const response = await fetch(URL);
if (!response.ok) {
throw new Error("데이터를 가져오는 데 실패했습니다.");
}
const jsonData = await response.json();
type === "assignees"
? setAssigneesData(jsonData)
: type === "labels"
? setLabelsData(jsonData)
: type === "milestones"
? setMilestonesData(jsonData)
: setAuthorsData(jsonData);
} catch (error) {
console.log("error");
}
};

fetchData();
}, []);

useEffect(() => {
const handleOutsideClick = (e: MouseEvent) => {
if (
dropdownRef.current &&
!dropdownRef.current.contains(e.target as Node)
) {
closeDropdown();
}
};

document.addEventListener("mousedown", handleOutsideClick);

return () => {
document.removeEventListener("mousedown", handleOutsideClick);
};
}, [closeDropdown]);

return (
<Container>
<Container ref={dropdownRef} $top={top} $left={left}>
<Header>
<Title>{title}</Title>
<Title>
{type === "filter"
? "이슈 필터"
: type === "assignees"
? "담당자 필터"
: type === "labels"
? "레이블 필터"
: type === "milestones"
? "마일스톤 필터"
: "작성자 필터"}
</Title>
</Header>
{assigneesList &&
assigneesList.assignees.map((assignee) => (
{type === "filter" && (
<div>
<DropdownItem itemName={"열린 이슈"} onClick={showOpenIssue} />
<DropdownItem
itemName={"내가 작성한 이슈"}
onClick={showAuthoredByMe}
/>
<DropdownItem
itemName={"나에게 할당된 이슈"}
onClick={showAssignedIssue}
/>
<DropdownItem
itemName={"내가 댓글을 남긴 이슈"}
onClick={showCommentedIssue}
/>
<DropdownItem itemName={"닫힌 이슈"} onClick={showCloseIssue} />
</div>
)}
{type === "assignees" &&
assigneesData &&
assigneesData.assignees!.map((assignee) => (
<DropdownItem
key={assignee.id}
id={assignee.id}
userImg={assignee.profileImageUrl}
itemName={assignee.nickname}
onClick={closeDropdown}
onClick={() => {
closeDropdown();
navigate(`/issues/assigneeId=${assignee.id}`);
}}
/>
))}
{type === "labels" &&
labelsData &&
labelsData.labels!.map((label) => (
<DropdownItem
key={label.id}
itemName={label.title}
onClick={() => {
closeDropdown();
navigate(`/issues/labelIds=${label.id}`);
}}
/>
))}
{type === "milestones" &&
milestonesData &&
milestonesData.milestones!.map((milestone) => (
<DropdownItem
key={milestone.id}
itemName={milestone.title}
onClick={() => {
closeDropdown();
navigate(`/issues/milestoneId=${milestone.id}`);
}}
/>
))}
{type === "authors" &&
authorsData &&
authorsData.authors!.map((author) => (
<DropdownItem
key={author.id}
itemName={author.nickname}
userImg={author.profileImageUrl}
onClick={() => {
closeDropdown();
navigate(`/issues/authorId=${author.id}`);
}}
/>
))}
</Container>
);
}

const Container = styled.div`
const Container = styled.div<{ $top: string; $left: string }>`
position: absolute;
z-index: 100;
top: 40px;
right: 0px;
z-index: 1000;
top: ${({ $top }) => $top};
left: ${({ $left }) => $left};
width: 240px;
background-color: ${({ theme }) => theme.colorSystem.neutral.surface.strong};
border: ${({ theme }) =>
`${theme.border.default} ${theme.colorSystem.neutral.border.default}`};
border-radius: ${({ theme }) => theme.radius.large};
Expand Down
2 changes: 1 addition & 1 deletion FE/src/components/FilterBar/FilterBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export default function FilterBar({
return (
<Wrapper>
<FilterButtonField>
<DropdownIndicator label={"필터"} />
<DropdownIndicator label={"필터"} dropdownTop={"48px"} />
</FilterButtonField>
<FilterInputField>
<FilterInputLabel htmlFor="filterInput">
Expand Down
Loading

0 comments on commit fc5b45e

Please sign in to comment.