From fc5b45e7475713d5995418f383b49a26da8e3ded Mon Sep 17 00:00:00 2001 From: litae <109706689+qkdflrgs@users.noreply.github.com> Date: Fri, 11 Aug 2023 12:08:14 +0900 Subject: [PATCH] =?UTF-8?q?[FE]=20refactor/#158:=20=EC=9D=B4=EC=8A=88=20?= =?UTF-8?q?=EB=AA=A9=EB=A1=9D=20=ED=8E=98=EC=9D=B4=EC=A7=80=20=ED=95=84?= =?UTF-8?q?=ED=84=B0=20=EA=B8=B0=EB=8A=A5=20=EB=A6=AC=ED=8C=A9=ED=86=A0?= =?UTF-8?q?=EB=A7=81=20(#159)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * refactor/#158: 메인 페이지 필터 드롭다운 인디케이터 수정 * refactor/#158: 필터 타입 추가 * refactor/#158: 인풋 컴포넌트 수정 * refactor/#158: 라벨 인풋 컴포넌트 스타일 수정 * refactor/#158: 드롭다운 인디케이터 컴포넌트 리팩토링 * refactor/#158: 드롭다운 아이템 컴포넌트 리팩토링 * refactor/#158: 드롭다운 패널 컴포넌트 리팩토링 * refactor/#158: 필터바 컴포넌트 내 드롭다운 수정 * refactor/#158: 레이블 리스트 컴포넌트 수정 * refactor/#158: 사이드바 컴포넌트 내부 인디케이터 수정 * refactor/#158: 레이블 상세 페이지 수정 --- .../DropdownIndicator/DropdownIndicator.tsx | 75 ++++++- .../components/DropdownPanel/DropdownItem.tsx | 14 +- .../DropdownPanel/DropdownPanel.tsx | 199 ++++++++++++++++-- FE/src/components/FilterBar/FilterBar.tsx | 2 +- FE/src/components/LabelList/LabelList.tsx | 8 +- FE/src/components/SideBar/SideBar.tsx | 3 + FE/src/components/common/Input/Input.tsx | 2 +- .../common/TextInput/LabelInput.tsx | 1 + FE/src/pages/LabelsPage.tsx | 8 +- FE/src/pages/MainPage.tsx | 17 +- FE/src/type.tsx | 7 +- 11 files changed, 289 insertions(+), 47 deletions(-) diff --git a/FE/src/components/DropdownIndicator/DropdownIndicator.tsx b/FE/src/components/DropdownIndicator/DropdownIndicator.tsx index 1ef85a102..17c3d0276 100644 --- a/FE/src/components/DropdownIndicator/DropdownIndicator.tsx +++ b/FE/src/components/DropdownIndicator/DropdownIndicator.tsx @@ -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(false); + + const openDropdownPanel = () => { + setOpenDropdown(true); + }; + + const closeDropdownPanel = () => { + setOpenDropdown(false); + }; + return ( - - {label} - - + + + {label} + + + {openDropdown && type === "filter" && ( + + )} + {openDropdown && type === "assignees" && ( + + )} + {openDropdown && type === "labels" && ( + + )} + {openDropdown && type === "milestones" && ( + + )} + {openDropdown && type === "authors" && ( + + )} + ); } @@ -28,7 +92,6 @@ const IndicatorButton = styled.button<{ $width: string; $height: string; }>` - position: relative; width: ${({ $width }) => $width}; height: ${({ $height }) => $height}; display: flex; @@ -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}; diff --git a/FE/src/components/DropdownPanel/DropdownItem.tsx b/FE/src/components/DropdownPanel/DropdownItem.tsx index 49e48d061..16da218dc 100644 --- a/FE/src/components/DropdownPanel/DropdownItem.tsx +++ b/FE/src/components/DropdownPanel/DropdownItem.tsx @@ -2,15 +2,13 @@ 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) { @@ -18,22 +16,20 @@ export default function DropdownItem({ {userImg && } - + ); } -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}`}; `; diff --git a/FE/src/components/DropdownPanel/DropdownPanel.tsx b/FE/src/components/DropdownPanel/DropdownPanel.tsx index 8ab323740..ca3a4c22b 100644 --- a/FE/src/components/DropdownPanel/DropdownPanel.tsx +++ b/FE/src/components/DropdownPanel/DropdownPanel.tsx @@ -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(null); + const [assigneesData, setAssigneesData] = useState( + null, + ); + const [labelsData, setLabelsData] = useState(null); + const [milestonesData, setMilestonesData] = useState( + null, + ); + const [authorsData, setAuthorsData] = useState(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 ( - +
- {title} + + {type === "filter" + ? "이슈 필터" + : type === "assignees" + ? "담당자 필터" + : type === "labels" + ? "레이블 필터" + : type === "milestones" + ? "마일스톤 필터" + : "작성자 필터"} +
- {assigneesList && - assigneesList.assignees.map((assignee) => ( + {type === "filter" && ( +
+ + + + + +
+ )} + {type === "assignees" && + assigneesData && + assigneesData.assignees!.map((assignee) => ( { + closeDropdown(); + navigate(`/issues/assigneeId=${assignee.id}`); + }} + /> + ))} + {type === "labels" && + labelsData && + labelsData.labels!.map((label) => ( + { + closeDropdown(); + navigate(`/issues/labelIds=${label.id}`); + }} + /> + ))} + {type === "milestones" && + milestonesData && + milestonesData.milestones!.map((milestone) => ( + { + closeDropdown(); + navigate(`/issues/milestoneId=${milestone.id}`); + }} + /> + ))} + {type === "authors" && + authorsData && + authorsData.authors!.map((author) => ( + { + closeDropdown(); + navigate(`/issues/authorId=${author.id}`); + }} /> ))}
); } -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}; diff --git a/FE/src/components/FilterBar/FilterBar.tsx b/FE/src/components/FilterBar/FilterBar.tsx index dc9ebc09d..c1b8173c8 100644 --- a/FE/src/components/FilterBar/FilterBar.tsx +++ b/FE/src/components/FilterBar/FilterBar.tsx @@ -16,7 +16,7 @@ export default function FilterBar({ return ( - + diff --git a/FE/src/components/LabelList/LabelList.tsx b/FE/src/components/LabelList/LabelList.tsx index e6b0db181..c6793f52c 100644 --- a/FE/src/components/LabelList/LabelList.tsx +++ b/FE/src/components/LabelList/LabelList.tsx @@ -1,5 +1,3 @@ -// type Props = {}; - import { styled } from "styled-components"; import Button from "../common/Button/Button"; import { Label } from "../../type"; @@ -41,7 +39,7 @@ export default function LabelList({ id, title, color, description }: Props) { setLabelDescription(e.target.value); }; - const handleCInputChange = (e: React.ChangeEvent) => { + const handleColorInputChange = (e: React.ChangeEvent) => { setLabelColor(e.target.value); }; @@ -76,7 +74,7 @@ export default function LabelList({ id, title, color, description }: Props) { }; return ( - + {!isEdit && ( @@ -108,7 +106,7 @@ export default function LabelList({ id, title, color, description }: Props) { color={labelColor} onChangeName={handleNameInputChange} onChangeDescrip={handleDescripInputChange} - onChangeColor={handleCInputChange} + onChangeColor={handleColorInputChange} randomColor={randomColor} cancelEdit={cancelEdit} confirmEdit={confirmEdit} diff --git a/FE/src/components/SideBar/SideBar.tsx b/FE/src/components/SideBar/SideBar.tsx index 812b30dae..c670455a2 100644 --- a/FE/src/components/SideBar/SideBar.tsx +++ b/FE/src/components/SideBar/SideBar.tsx @@ -6,6 +6,7 @@ export default function SideBar() { theme.font.displayMedium16}; diff --git a/FE/src/components/common/TextInput/LabelInput.tsx b/FE/src/components/common/TextInput/LabelInput.tsx index 44eeca2c1..a7519b6c0 100644 --- a/FE/src/components/common/TextInput/LabelInput.tsx +++ b/FE/src/components/common/TextInput/LabelInput.tsx @@ -56,6 +56,7 @@ const Label = styled.label` width: 64px; font: ${({ theme }) => theme.font.displayMedium12}; color: ${({ theme }) => theme.colorSystem.neutral.text.weak}; + white-space: nowrap; `; const RandomButton = styled.button` diff --git a/FE/src/pages/LabelsPage.tsx b/FE/src/pages/LabelsPage.tsx index 999bcb2c9..32d92b958 100644 --- a/FE/src/pages/LabelsPage.tsx +++ b/FE/src/pages/LabelsPage.tsx @@ -21,7 +21,7 @@ export default function LabelsPage() { }; const goMilestonesPage = () => { - navigate("/milestones"); + navigate("/milestones/isOpen=true"); }; const openDelete = () => { @@ -110,7 +110,7 @@ export default function LabelsPage() {