From a23499d4d692a519c9591cfb0c9bae2411de9d1e Mon Sep 17 00:00:00 2001 From: "SK\\ssssk" Date: Thu, 15 Aug 2024 19:43:41 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20=EB=92=A4=EB=A1=9C=EA=B0=80=EA=B8=B0=20?= =?UTF-8?q?=EC=8B=9C=20=EC=A3=BC=EC=86=8C=EC=97=90=20=EB=94=B0=EB=9D=BC=20?= =?UTF-8?q?=ED=95=84=ED=84=B0=20=EA=B4=80=EB=A0=A8=20UI=20=EB=A0=8C?= =?UTF-8?q?=EB=8D=94=EB=A7=81=EB=90=98=EA=B2=8C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/common/dropdown-down-arrow.svg | 3 + src/components/common/dropdown/Dropdown.tsx | 37 +++++-- .../gathering/GatheringCardList.tsx | 21 ++++ .../gathering/GatheringFilterModal.tsx | 16 ++- src/components/gathering/GatheringList.tsx | 104 ++++-------------- src/components/gathering/GatheringSearch.tsx | 35 ++++++ .../gathering/GatheringSubCategoryList.tsx | 29 ----- .../gathering/GatheringCardListContainer.tsx | 40 +++++++ .../GatheringCategoryListContainer.tsx | 51 +++++++++ .../GatheringExcludeCompleteContainer.tsx | 46 ++++++++ .../gathering/GatheringFilterContainer.tsx | 26 +++++ .../gathering/GatheringListContainer.tsx | 85 -------------- .../gathering/GatheringSearchContainer.tsx | 39 +++++++ .../gathering/GatheringSortContainer.tsx | 53 +++++++++ 14 files changed, 376 insertions(+), 209 deletions(-) create mode 100644 public/common/dropdown-down-arrow.svg create mode 100644 src/components/gathering/GatheringCardList.tsx create mode 100644 src/components/gathering/GatheringSearch.tsx delete mode 100644 src/components/gathering/GatheringSubCategoryList.tsx create mode 100644 src/containers/gathering/GatheringCardListContainer.tsx create mode 100644 src/containers/gathering/GatheringCategoryListContainer.tsx create mode 100644 src/containers/gathering/GatheringExcludeCompleteContainer.tsx create mode 100644 src/containers/gathering/GatheringFilterContainer.tsx create mode 100644 src/containers/gathering/GatheringSearchContainer.tsx create mode 100644 src/containers/gathering/GatheringSortContainer.tsx diff --git a/public/common/dropdown-down-arrow.svg b/public/common/dropdown-down-arrow.svg new file mode 100644 index 00000000..538183db --- /dev/null +++ b/public/common/dropdown-down-arrow.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/components/common/dropdown/Dropdown.tsx b/src/components/common/dropdown/Dropdown.tsx index 8b79d00f..c7f394ed 100644 --- a/src/components/common/dropdown/Dropdown.tsx +++ b/src/components/common/dropdown/Dropdown.tsx @@ -1,15 +1,30 @@ -import { useState } from "react"; -import { IoIosArrowDown } from "react-icons/io"; +import useOutsideClick from "@/hooks/useOutsideClick"; +import Image from "next/image"; +import { useEffect, useRef, useState } from "react"; interface IDropdown { options: { value: T, name: string }[]; dropdownHandler: (value: T) => void; defaultValue: T; + value: T; } -export default function Dropdown({ options, dropdownHandler, defaultValue }: IDropdown) { +export default function Dropdown({ options, dropdownHandler, defaultValue, value }: IDropdown) { const [isOpen, setIsOpen] = useState(false); const [selectedOption, setSelectedOption] = useState(defaultValue); + const [documentBody, setDocumentBody] = useState(null); + const ref = useRef(null); + + useEffect(() => { + setDocumentBody(document.body); + }, []); + useOutsideClick(ref, () => { + setIsOpen(false); + }); + + useEffect(() => { + setSelectedOption(value); + },[value]) const toggleDropdown = () => { setIsOpen(!isOpen); @@ -21,22 +36,26 @@ export default function Dropdown({ options, dropdownHandler, defaultValue }: }; return ( -
+
{isOpen && (
-
+
{options.map((i) => ( - +
- - props.closeModal()}> - props.closeModal()} /> - - + + +
+
+
-
-
- - +
+ +
+ +
-
- -
+
); }; diff --git a/src/components/gathering/GatheringSearch.tsx b/src/components/gathering/GatheringSearch.tsx new file mode 100644 index 00000000..ee60090b --- /dev/null +++ b/src/components/gathering/GatheringSearch.tsx @@ -0,0 +1,35 @@ +interface IGatheringSearch { + keywordRef: React.RefObject; + searchHandler: (value: string) => void; +} +const GatheringSearch = (props: IGatheringSearch) => { + return ( + + ); +}; +export default GatheringSearch \ No newline at end of file diff --git a/src/components/gathering/GatheringSubCategoryList.tsx b/src/components/gathering/GatheringSubCategoryList.tsx deleted file mode 100644 index 44c862c7..00000000 --- a/src/components/gathering/GatheringSubCategoryList.tsx +++ /dev/null @@ -1,29 +0,0 @@ - - -import { GatheringCategoryListType } from "@/types/GatheringCategoryDto"; - -interface IGatheringSubCategoryList { - gatheringCategoryList: GatheringCategoryListType; - activeGatheringCategoryId: number; - changeGatheringCategoryHandler: (_id: number) => void; -} - -const GatheringSubCategoryList = ({ gatheringCategoryList, activeGatheringCategoryId, changeGatheringCategoryHandler }: IGatheringSubCategoryList) => { - - return ( -
- - {gatheringCategoryList.map((i) => ( - - ))} -
- ); -}; - -export default GatheringSubCategoryList; diff --git a/src/containers/gathering/GatheringCardListContainer.tsx b/src/containers/gathering/GatheringCardListContainer.tsx new file mode 100644 index 00000000..929ed76c --- /dev/null +++ b/src/containers/gathering/GatheringCardListContainer.tsx @@ -0,0 +1,40 @@ +import GatheringCardList from "@/components/gathering/GatheringCardList"; +import UrlQueryStringToObject from "@/utils/UrlQueryStringToObject"; +import { useSearchParams } from "next/navigation"; +import { useEffect, useState } from "react"; +import PaginationContainer from "../common/PaginationContainer"; + +interface IGatheringCardListContainer { + +} +const GatheringCardListContainer = (props: IGatheringCardListContainer) => { + const searchParams = useSearchParams(); + const [page, setPage] = useState(searchParams.get('page') || 0); + const [totalData, setTotalData] = useState(10); + const changeGatheringPageHandler = (id: number) => { + let _url = `/gathering?`; + let temp = UrlQueryStringToObject(window.location.href) || {}; + if (page != 0) { + temp.page = id; + } + Object.entries(temp).map(i => { + _url += i[0]+"="+i[1]+"&" + }) + if (_url.endsWith("&")) { + _url = _url.slice(0, -1); + } + console.log("GatheringListContainer.tsx 파일 : ", _url); + window.history.pushState(null, "", _url); + } + + useEffect(() => { + + }, [searchParams]) + return ( +
+ + +
+ ); +}; +export default GatheringCardListContainer \ No newline at end of file diff --git a/src/containers/gathering/GatheringCategoryListContainer.tsx b/src/containers/gathering/GatheringCategoryListContainer.tsx new file mode 100644 index 00000000..82766389 --- /dev/null +++ b/src/containers/gathering/GatheringCategoryListContainer.tsx @@ -0,0 +1,51 @@ +import { GatheringCategoryListType } from "@/types/GatheringCategoryDto"; +import UrlQueryStringToObject from "@/utils/UrlQueryStringToObject"; +import { useSearchParams } from "next/navigation"; +import { useEffect, useState } from "react"; + +interface IGatheringCategoryListContainer { + gatheringCategoryList: GatheringCategoryListType; +} +const GatheringCategoryListContainer = ({ gatheringCategoryList }: IGatheringCategoryListContainer) => { + const [activeGatheringCategoryId, setActiveGatheringCategoryId] = useState(0); + const searchParams = useSearchParams(); + const changeGatheringCategoryHandler = (id: number) => { + setActiveGatheringCategoryId(id); + let _url = `/gathering?`; + let temp = UrlQueryStringToObject(window.location.href) || {}; + delete temp.gatheringCategoryId; + if (id != 0) { + temp.gatheringCategoryId = id; + } + Object.entries(temp).map(i => { + _url += i[0]+"="+i[1]+"&" + }) + if (_url.endsWith("&")) { + _url = _url.slice(0, -1); + } + console.log("GatheringListContainer.tsx 파일 : ", _url); + window.history.pushState(null, "", _url); + } + + useEffect(() => { + setActiveGatheringCategoryId(+(searchParams.get('gatheringCategoryId') || 0)); + }, [searchParams]) + + return ( +
+
+ + {gatheringCategoryList.map((i) => ( + + ))} +
+
+ ); +}; +export default GatheringCategoryListContainer \ No newline at end of file diff --git a/src/containers/gathering/GatheringExcludeCompleteContainer.tsx b/src/containers/gathering/GatheringExcludeCompleteContainer.tsx new file mode 100644 index 00000000..ba40dd85 --- /dev/null +++ b/src/containers/gathering/GatheringExcludeCompleteContainer.tsx @@ -0,0 +1,46 @@ +import UrlQueryStringToObject from "@/utils/UrlQueryStringToObject"; +import Image from "next/image"; +import { useSearchParams } from "next/navigation"; +import { useEffect, useState } from "react"; + +interface IGatheringExcludeCompleteContainer { + +} +const GatheringExcludeCompleteContainer = (props: IGatheringExcludeCompleteContainer) => { + + const [isExcludeCompleted, setIsExcludeCompleted] = useState(true); + const searchParams = useSearchParams(); + const checkExcludeCompleteGatheringHandler = () => { + setIsExcludeCompleted(prev => !prev); + let _url = `/gathering?`; + let temp = UrlQueryStringToObject(window.location.href) || {}; + delete temp.isExclude; + if (isExcludeCompleted) { + temp.isExclude = "false"; + } + Object.entries(temp).map(i => { + _url += i[0]+"="+i[1]+"&" + }) + if (_url.endsWith("&")) { + _url = _url.slice(0, -1); + } + console.log("GatheringListContainer.tsx 파일 : ", _url); + window.history.pushState(null, "", _url); + } + + useEffect(() => { + setIsExcludeCompleted(searchParams.get('isExclude') ? false : true); + },[searchParams]) + + return ( + + ); +}; +export default GatheringExcludeCompleteContainer \ No newline at end of file diff --git a/src/containers/gathering/GatheringFilterContainer.tsx b/src/containers/gathering/GatheringFilterContainer.tsx new file mode 100644 index 00000000..12c52549 --- /dev/null +++ b/src/containers/gathering/GatheringFilterContainer.tsx @@ -0,0 +1,26 @@ +import { Modal } from "@/components/common/modal/Modal"; +import GatheringFilterModal from "@/components/gathering/GatheringFilterModal"; +import { useState } from "react"; +import { VscSettings } from "react-icons/vsc"; + +interface IGatheringFilterContainer { + +} +const GatheringFilterContainer = (props: IGatheringFilterContainer) => { + const [isModal, setIsModal] = useState(false); + return ( + <> + + setIsModal(false)}> + setIsModal(false)} /> + + + ); +}; +export default GatheringFilterContainer \ No newline at end of file diff --git a/src/containers/gathering/GatheringListContainer.tsx b/src/containers/gathering/GatheringListContainer.tsx index 7a9ab9fd..156fb217 100644 --- a/src/containers/gathering/GatheringListContainer.tsx +++ b/src/containers/gathering/GatheringListContainer.tsx @@ -2,8 +2,6 @@ import GatheringList from "@/components/gathering/GatheringList"; import { GatheringCategoryListType } from "@/types/GatheringCategoryDto"; -import UrlQueryStringToObject from "@/utils/UrlQueryStringToObject"; -import { useRef, useState } from "react"; interface IgatheringCategoryList { gatheringCategoryList: GatheringCategoryListType; @@ -11,93 +9,10 @@ interface IgatheringCategoryList { } const GatheringListContainer = ({gatheringCategoryList, sortDefaultValue}:IgatheringCategoryList) => { - const [isModal, setIsModal] = useState(false); - const [isExcludeCompleted, setIsExcludeCompleted] = useState(true) - const [activeGatheringCategoryId, setActiveGatheringCategoryId] = useState(0); - const keywordRef = useRef(null); - const checkExcludeCompleteGatheringHandler = () => { - setIsExcludeCompleted(prev => !prev); - let _url = `/gathering?`; - let temp = UrlQueryStringToObject(window.location.href) || {}; - delete temp.isExclude; - if (isExcludeCompleted) { - temp.isExclude = "false"; - } - Object.entries(temp).map(i => { - _url += i[0]+"="+i[1]+"&" - }) - if (_url.endsWith("&")) { - _url = _url.slice(0, -1); - } - console.log("GatheringListContainer.tsx 파일 : ", _url); - window.history.pushState(null, "", _url); - } - const changeGatheringCategoryHandler = (id: number) => { - setActiveGatheringCategoryId(id); - let _url = `/gathering?`; - let temp = UrlQueryStringToObject(window.location.href) || {}; - delete temp.gatheringCategoryId; - if (id != 0) { - temp.gatheringCategoryId = id; - } - Object.entries(temp).map(i => { - _url += i[0]+"="+i[1]+"&" - }) - if (_url.endsWith("&")) { - _url = _url.slice(0, -1); - } - console.log("GatheringListContainer.tsx 파일 : ", _url); - window.history.pushState(null, "", _url); - } - - const sortHandler = (value: string) => { - let _url = `/gathering?`; - let temp = UrlQueryStringToObject(window.location.href) || {}; - delete temp.sort; - if (value != "") { - temp.sort = value; - } - Object.entries(temp).map(i => { - _url += i[0]+"="+i[1]+"&" - }) - if (_url.endsWith("&")) { - _url = _url.slice(0, -1); - } - console.log("GatheringListContainer.tsx 파일 : ", _url); - window.history.pushState(null, "", _url); - } - - const searchHandler = (value: string) => { - let _url = `/gathering?`; - let temp = UrlQueryStringToObject(window.location.href) || {}; - delete temp.keyword; - if (value != "") { - temp.keyword = value; - } - Object.entries(temp).map(i => { - _url += i[0]+"="+i[1]+"&" - }) - if (_url.endsWith("&")) { - _url = _url.slice(0, -1); - } - console.log("GatheringListContainer.tsx 파일 : ", _url); - window.history.pushState(null, "", _url); - } return ( setIsModal(false)} - openModal={() => setIsModal(true)} gatheringCategoryList={gatheringCategoryList} - isExcludeCompleted={isExcludeCompleted} - activeGatheringCategoryId={activeGatheringCategoryId} - checkExcludeCompleteGatheringHandler={checkExcludeCompleteGatheringHandler} - changeGatheringCategoryHandler={changeGatheringCategoryHandler} - sortHandler={sortHandler} - searchHandler={searchHandler} - keywordRef={keywordRef} - sortDefaultValue={sortDefaultValue} /> ); }; diff --git a/src/containers/gathering/GatheringSearchContainer.tsx b/src/containers/gathering/GatheringSearchContainer.tsx new file mode 100644 index 00000000..49f9dae8 --- /dev/null +++ b/src/containers/gathering/GatheringSearchContainer.tsx @@ -0,0 +1,39 @@ +import GatheringSearch from "@/components/gathering/GatheringSearch"; +import UrlQueryStringToObject from "@/utils/UrlQueryStringToObject"; +import { useSearchParams } from "next/navigation"; +import { useEffect, useRef } from "react"; + +interface IGatheringSearchContainer { + +} +const GatheringSearchContainer = (props: IGatheringSearchContainer) => { + const keywordRef = useRef(null); + const searchParams = useSearchParams(); + const searchHandler = (value: string) => { + let _url = `/gathering?`; + let temp = UrlQueryStringToObject(window.location.href) || {}; + delete temp.keyword; + if (value != "") { + temp.keyword = value; + } + Object.entries(temp).map(i => { + _url += i[0]+"="+i[1]+"&" + }) + if (_url.endsWith("&")) { + _url = _url.slice(0, -1); + } + window.history.pushState(null, "", _url); + } + + + useEffect(() => { + if (keywordRef.current) { + keywordRef.current.value = searchParams.get('keyword') || ''; + } + },[searchParams]) + + return ( + + ); +}; +export default GatheringSearchContainer \ No newline at end of file diff --git a/src/containers/gathering/GatheringSortContainer.tsx b/src/containers/gathering/GatheringSortContainer.tsx new file mode 100644 index 00000000..73e9f0c7 --- /dev/null +++ b/src/containers/gathering/GatheringSortContainer.tsx @@ -0,0 +1,53 @@ +import Dropdown from "@/components/common/dropdown/Dropdown"; +import UrlQueryStringToObject from "@/utils/UrlQueryStringToObject"; +import { useSearchParams } from "next/navigation"; +import { useEffect, useState } from "react"; + +interface IGatheringSortContainer { + +} + +const OPTIONS = [{ + value: "", + name: "최신순", +}, { + value: "likes", + name: "인기순", +}, { + value: "views", + name: "조회순", +}]; + +const GatheringSortContainer = (props: IGatheringSortContainer) => { + const searchParams = useSearchParams(); + const [sort, setSort] = useState(""); + const sortHandler = (value: string) => { + let _url = `/gathering?`; + let temp = UrlQueryStringToObject(window.location.href) || {}; + delete temp.sort; + if (value != "") { + temp.sort = value; + } + Object.entries(temp).map(i => { + _url += i[0]+"="+i[1]+"&" + }) + if (_url.endsWith("&")) { + _url = _url.slice(0, -1); + } + console.log("GatheringListContainer.tsx 파일 : ", _url); + window.history.pushState(null, "", _url); + } + + useEffect(() => { + setSort(searchParams.get('sort') || "") + },[searchParams]) + + + return ( + <> + + + ); +}; +export default GatheringSortContainer +