From 1df205c5345b5a7ee54083f2af9eb255d01e89f4 Mon Sep 17 00:00:00 2001 From: inyoung Date: Thu, 18 Apr 2024 22:01:08 +0900 Subject: [PATCH 01/18] Style: Divder2 makrup #229 --- src/components/Common/Divider2.tsx | 70 +++++++++++++++++++++++++ src/pages/Buyer/BuyerPayment.tsx | 2 +- src/pages/Buyer/BuyerSavedCounselor.tsx | 9 ++-- 3 files changed, 77 insertions(+), 4 deletions(-) create mode 100644 src/components/Common/Divider2.tsx diff --git a/src/components/Common/Divider2.tsx b/src/components/Common/Divider2.tsx new file mode 100644 index 00000000..06f84e8b --- /dev/null +++ b/src/components/Common/Divider2.tsx @@ -0,0 +1,70 @@ +import React, { useState } from 'react'; +import styled from 'styled-components'; +import { Black, Green, Grey4, Grey6 } from 'styles/color'; +import { ReactComponent as UnderLineBuyer } from 'assets/icons/underline-buyer.svg'; +import { ReactComponent as UnderLineBuyerBig } from 'assets/icons/underline-big.svg'; +import { Subtitle } from 'styles/font'; +interface Divder2Props { + tabState: number; + setTabState: React.Dispatch>; +} +function Divider2({ tabState, setTabState }: Divder2Props) { + return ( + + { + setTabState(1); + }} + > + {tabState === 1 ? ( + <> + 상담사 + + + ) : ( + 상담사 + )} + + { + setTabState(2); + }} + > + {tabState === 2 ? ( + <> + 공개상담 + + + ) : ( + 공개상담 + )} + + + ); +} + +const Wrapper = styled.nav` + width: 100%; + box-sizing: border-box; + padding-top: 0.8rem; + height: 4.4rem; + display: flex; + justify-content: center; + border-bottom: 1px solid ${Grey6}; + position: sticky; + top: 5.3rem; + background-color: white; + z-index: 999; +`; + +const TabButton = styled.div<{ tabState: number }>` + display: flex; + flex-direction: column; + width: 50%; + align-items: center; + cursor: pointer; +`; + +export default Divider2; diff --git a/src/pages/Buyer/BuyerPayment.tsx b/src/pages/Buyer/BuyerPayment.tsx index 6d14438b..536743ae 100644 --- a/src/pages/Buyer/BuyerPayment.tsx +++ b/src/pages/Buyer/BuyerPayment.tsx @@ -225,7 +225,7 @@ export const BuyerPayment = () => { ) : null} - ); + ); } }; diff --git a/src/pages/Buyer/BuyerSavedCounselor.tsx b/src/pages/Buyer/BuyerSavedCounselor.tsx index 3d45e494..bf801232 100644 --- a/src/pages/Buyer/BuyerSavedCounselor.tsx +++ b/src/pages/Buyer/BuyerSavedCounselor.tsx @@ -10,15 +10,15 @@ import { WishlistDataType } from 'utils/type'; import { ReactComponent as Empty } from 'assets/icons/graphic-noting.svg'; import styled from 'styled-components'; import useIntersectionObserver from 'hooks/useIntersectionObserver'; +import Divider2 from 'components/Common/Divider2'; // TODO: 찜한 마인더 없을 시 페이지 추후 백 연동 시 구현 export const BuyerSavedCounselor = () => { const navigate = useNavigate(); const [isInitialLoading, setIsInitialLoading] = useState(true); const [wishlistData, setWishlistData] = useState([]); const [isLastElem, setIsLastElem] = useState(false); - + const [tabState, setTabState] = useState(1); const preventRef = useRef(true); - const onIntersect: IntersectionObserverCallback = async (entry) => { if ( entry[0].isIntersecting && @@ -85,6 +85,7 @@ export const BuyerSavedCounselor = () => { /> 찜 목록 + ); } else { @@ -99,10 +100,11 @@ export const BuyerSavedCounselor = () => { /> 찜 목록 +
{!isLastElem ? ( @@ -124,6 +126,7 @@ export const BuyerSavedCounselor = () => { /> 찜 목록 + 아직 후기가 없어요. From 90cf37a193706430b2ba83af921cbed00010f596 Mon Sep 17 00:00:00 2001 From: inyoung Date: Thu, 18 Apr 2024 22:55:17 +0900 Subject: [PATCH 02/18] =?UTF-8?q?Feat:=20=EC=B0=9C=20=EB=AA=A9=EB=A1=9D=20?= =?UTF-8?q?=ED=8E=98=EC=9D=B4=EC=A7=80=20api=20=EC=97=B0=EB=8F=99=20?= =?UTF-8?q?=EB=B0=8F=20=EB=AC=B4=ED=95=9C=EC=8A=A4=ED=81=AC=EB=A1=A4=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84=20=EC=99=84=EB=A3=8C=20#228?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/get.ts | 5 + .../SavedCounselorCard.tsx | 6 +- .../SavedOpenConsultCard.tsx | 111 +++++++++ .../SavedOpenConsultResults.tsx | 28 +++ src/pages/Buyer/BuyerConsult.tsx | 2 + src/pages/Buyer/BuyerSavedCounselor.tsx | 210 +++++++++++++----- 6 files changed, 309 insertions(+), 53 deletions(-) create mode 100644 src/components/Buyer/BuyerSavedCounselor.tsx/SavedOpenConsultCard.tsx create mode 100644 src/components/Buyer/BuyerSavedCounselor.tsx/SavedOpenConsultResults.tsx diff --git a/src/api/get.ts b/src/api/get.ts index c8fb34ee..8e5f0829 100644 --- a/src/api/get.ts +++ b/src/api/get.ts @@ -178,3 +178,8 @@ export const getCustomerIsWriter = async (postId: any) => export const getOpenConsultDraft = async (postId: any) => await getInstance(`/posts/drafts/${postId}`); + +// Post Scrap Controller + +export const getPostScraps = async (params: any) => + getInstance(`/postScraps`, params); diff --git a/src/components/Buyer/BuyerSavedCounselor.tsx/SavedCounselorCard.tsx b/src/components/Buyer/BuyerSavedCounselor.tsx/SavedCounselorCard.tsx index c3c7e528..0e0f0754 100644 --- a/src/components/Buyer/BuyerSavedCounselor.tsx/SavedCounselorCard.tsx +++ b/src/components/Buyer/BuyerSavedCounselor.tsx/SavedCounselorCard.tsx @@ -83,8 +83,10 @@ export const SavedCounselorCard = ({ }} > - {tagList.map((value: any) => { - return ; + {tagList.map((value: any, index) => { + return ( + + ); })} {introduction} diff --git a/src/components/Buyer/BuyerSavedCounselor.tsx/SavedOpenConsultCard.tsx b/src/components/Buyer/BuyerSavedCounselor.tsx/SavedOpenConsultCard.tsx new file mode 100644 index 00000000..a8f2803a --- /dev/null +++ b/src/components/Buyer/BuyerSavedCounselor.tsx/SavedOpenConsultCard.tsx @@ -0,0 +1,111 @@ +import React, { useEffect, useLayoutEffect, useRef, useState } from 'react'; +import styled from 'styled-components'; +import { Green, Grey1, Grey2, Grey3, Grey6 } from 'styles/color'; +import { Body1, Caption1, Caption2 } from 'styles/font'; +import { LoadingSpinner } from 'utils/LoadingSpinner'; +import { ReactComponent as LockIcon } from 'assets/icons/icon-lock.svg'; +import { ReactComponent as HeartIcon } from 'assets/icons/icon-heart2.svg'; +import { ReactComponent as HeartEmptyIcon } from 'assets/icons/icon-heart3.svg'; +import { ReactComponent as SaveIcon } from 'assets/icons/icon-save2.svg'; +import { ReactComponent as SaveEmptyIcon } from 'assets/icons/icon-save3.svg'; +import { ReactComponent as CommentIcon } from 'assets/icons/icon-comment.svg'; +import { ReactComponent as CheckIcon } from 'assets/icons/icon-check2.svg'; +import { ReactComponent as WriteIcon } from 'assets/icons/icon-write.svg'; +import { Space } from 'components/Common/Space'; +import { BackDrop } from 'components/Common/BackDrop'; +import { openConsultApiObject } from 'pages/Buyer/BuyerConsult'; +import { useNavigate } from 'react-router-dom'; +import useIntersectionObserver from 'hooks/useIntersectionObserver'; +interface SavedCounselorCardProps { + item: openConsultApiObject; +} +function SavedOpenConsultCard({ item }: SavedCounselorCardProps) { + const navigate = useNavigate(); + return ( + { + navigate(`/open-consult/${item.postId}`); + }} + > +
+ {item.title} +
+ +
{item.content}
+
+ + + {item.totalLike} + + + + {item.totalScrap} + + + + {item.totalComment} + +
+ {item.updatedAt} +
+ ); +} + +const Wrapper = styled.div` + width: 89%; + height: 14rem; + cursor: pointer; + position: relative; + background-color: ${Grey6}; + padding: 1.6rem; + box-sizing: border-box; + border-radius: 1.2rem; + .row1 { + width: calc(100% - 5rem); + display: -webkit-box; + -webkit-box-orient: vertical; + -webkit-line-clamp: 1; + max-height: 5rem; + overflow: hidden; + } + .row2 { + display: -webkit-box; + max-height: 4.7rem; + -webkit-box-orient: vertical; + overflow: hidden; + align-self: flex-end; + margin-bottom: 0.4rem; + -webkit-line-clamp: 2; + color: ${Grey1}; + height: 4.6rem; + text-overflow: ellipsis; + font-family: Pretendard; + font-size: 1.4rem; + font-style: normal; + font-weight: 400; + line-height: 155%; + } + .row3 { + display: flex; + gap: 1.2rem; + } +`; +const HeartResizeIcon = styled(HeartIcon)` + width: 2rem; + height: 2rem; +`; +const IconItem = styled.div` + display: flex; + align-items: center; + gap: 0.5rem; +`; +const TimeLeft = styled.div` + font-size: 1.2rem; + font-weight: 400; + color: ${Grey2}; + position: absolute; + bottom: 1.8rem; + right: 1.6rem; +`; + +export default SavedOpenConsultCard; diff --git a/src/components/Buyer/BuyerSavedCounselor.tsx/SavedOpenConsultResults.tsx b/src/components/Buyer/BuyerSavedCounselor.tsx/SavedOpenConsultResults.tsx new file mode 100644 index 00000000..a506d482 --- /dev/null +++ b/src/components/Buyer/BuyerSavedCounselor.tsx/SavedOpenConsultResults.tsx @@ -0,0 +1,28 @@ +import { openConsultApiObject } from 'pages/Buyer/BuyerConsult'; +import React from 'react'; +import styled from 'styled-components'; +import SavedOpenConsultCard from './SavedOpenConsultCard'; + +interface SavedOpenConsultResultsProps { + openConsultList: openConsultApiObject[]; +} +function SavedOpenConsultResults({ + openConsultList, +}: SavedOpenConsultResultsProps) { + return ( + + {openConsultList.map((card) => ( + + ))} + + ); +} + +const Wrapper = styled.div` + display: flex; + flex-direction: column; + gap: 1.2rem; + align-items: center; + width: 100%; +`; +export default SavedOpenConsultResults; diff --git a/src/pages/Buyer/BuyerConsult.tsx b/src/pages/Buyer/BuyerConsult.tsx index 36b784fd..643654e6 100644 --- a/src/pages/Buyer/BuyerConsult.tsx +++ b/src/pages/Buyer/BuyerConsult.tsx @@ -30,6 +30,7 @@ export interface consultApiObject { } export interface openConsultApiObject { postId: number; + postScrapId: number; title: string; content: string; isPublic: boolean; @@ -39,6 +40,7 @@ export interface openConsultApiObject { publishedAt: string; isChosen: boolean; isScrapped: boolean; + scrappedAt: string; totalScrap: number; totalComment: number; updatedAt: string; diff --git a/src/pages/Buyer/BuyerSavedCounselor.tsx b/src/pages/Buyer/BuyerSavedCounselor.tsx index bf801232..d08fa37d 100644 --- a/src/pages/Buyer/BuyerSavedCounselor.tsx +++ b/src/pages/Buyer/BuyerSavedCounselor.tsx @@ -11,11 +11,18 @@ import { ReactComponent as Empty } from 'assets/icons/graphic-noting.svg'; import styled from 'styled-components'; import useIntersectionObserver from 'hooks/useIntersectionObserver'; import Divider2 from 'components/Common/Divider2'; +import { openConsultApiObject } from './BuyerConsult'; +import { getPostScraps } from 'api/get'; +import SavedOpenConsultResults from 'components/Buyer/BuyerSavedCounselor.tsx/SavedOpenConsultResults'; +import { LoadingSpinner } from 'utils/LoadingSpinner'; // TODO: 찜한 마인더 없을 시 페이지 추후 백 연동 시 구현 export const BuyerSavedCounselor = () => { const navigate = useNavigate(); const [isInitialLoading, setIsInitialLoading] = useState(true); const [wishlistData, setWishlistData] = useState([]); + const [openConsultData, setOpenConsultData] = useState< + openConsultApiObject[] + >([]); const [isLastElem, setIsLastElem] = useState(false); const [tabState, setTabState] = useState(1); const preventRef = useRef(true); @@ -27,10 +34,18 @@ export const BuyerSavedCounselor = () => { preventRef.current ) { preventRef.current = false; - await fetchWishlistData( - wishlistData[wishlistData.length - 1].wishlistId, - wishlistData[wishlistData.length - 1].updatedAt, - ); + if (tabState === 1) { + await fetchWishlistData( + wishlistData[wishlistData.length - 1].wishlistId, + wishlistData[wishlistData.length - 1].updatedAt, + ); + } else if (tabState === 2) { + await fetchOpenConsultData( + openConsultData[openConsultData.length - 1].postScrapId, + openConsultData[openConsultData.length - 1].scrappedAt, + ); + } + preventRef.current = true; } }; @@ -46,12 +61,12 @@ export const BuyerSavedCounselor = () => { wishlistId: lastId, updatedAt: lastUpdateAt, }; - try { const res: any = await postWishLists(body); if (res.status === 200) { if (res.data.length !== 0) { if (lastId === 0) { + setIsInitialLoading(true); setWishlistData(res.data); } else { const updatedReviews = [...wishlistData, ...res.data]; @@ -71,9 +86,44 @@ export const BuyerSavedCounselor = () => { } } }; + const fetchOpenConsultData = async (lastId: number, lastUpdateAt: string) => { + try { + const params = { + postScrapId: lastId, + scrappedAt: lastUpdateAt, + }; + const res: any = await getPostScraps({ params }); + if (res.status === 200) { + if (res.data.length !== 0) { + if (lastId === 0) { + setIsInitialLoading(true); + setOpenConsultData(res.data); + } else { + const updatedOpenConsultList = [...openConsultData, ...res.data]; + setOpenConsultData(updatedOpenConsultList); + } + } else { + setIsLastElem(true); + } + } else if (res.response.status !== 401) { + // navigate('/mypage'); + } + } catch (err) { + alert(err); + } finally { + if (lastId === 0) { + setIsInitialLoading(false); + } + } + }; useLayoutEffect(() => { - fetchWishlistData(0, ''); - }, []); + setIsInitialLoading(true); + if (tabState === 1) { + fetchWishlistData(0, ''); + } else if (tabState === 2) { + fetchOpenConsultData(0, new Date().toISOString().slice(0, 19)); + } + }, [tabState]); if (isInitialLoading) { return ( <> @@ -86,53 +136,111 @@ export const BuyerSavedCounselor = () => { 찜 목록 +
+ +
); } else { - if (wishlistData.length !== 0) { - return ( - <> - - { - navigate('/mypage'); - }} - /> - 찜 목록 - - - -
- - {!isLastElem ? ( -
- ) : ( -
- )} -
- - ); - } else { - return ( - <> - - { - navigate('/mypage'); - }} - /> - 찜 목록 - - - - - 아직 후기가 없어요. - - - ); + if (tabState === 1) { + if (wishlistData.length !== 0) { + return ( + <> + + { + navigate('/mypage'); + }} + /> + 찜 목록 + + + +
+ + {!isLastElem ? ( +
+ ) : ( +
+ )} +
+ + ); + } else { + return ( + <> + + { + navigate('/mypage'); + }} + /> + 찜 목록 + + + + + 아직 후기가 없어요. + + + ); + } + } else if (tabState === 2) { + if (openConsultData.length !== 0) { + return ( + <> + + { + navigate('/mypage'); + }} + /> + 찜 목록 + + + +
+ + {!isLastElem ? ( +
+ ) : ( +
+ )} +
+ + ); + } else { + return ( + <> + + { + navigate('/mypage'); + }} + /> + 찜 목록 + + + + + 아직 저장한 일대다상담이 없어요. + + + ); + } } } }; From 1419d584b1aeca91b662274c222372c8c2fd543e Mon Sep 17 00:00:00 2001 From: inyoung Date: Thu, 18 Apr 2024 23:08:59 +0900 Subject: [PATCH 03/18] =?UTF-8?q?Fix:=20=EC=85=B0=EC=96=B4=20=ED=99=88?= =?UTF-8?q?=ED=99=94=EB=A9=B4=20=EB=93=A4=EC=A4=80=EB=A7=88,=20=EC=A7=84?= =?UTF-8?q?=EC=A4=91=EC=83=81=20=EB=B0=94=EB=A1=9C=EA=B0=80=EA=B8=B0=20?= =?UTF-8?q?=ED=86=A0=EA=B8=80=20=EB=B2=84=ED=8A=BC=20=EC=8A=A4=ED=83=80?= =?UTF-8?q?=EC=9D=BC=EC=97=90=20=ED=86=B5=EC=9D=BC=EC=84=B1=20=EB=B6=80?= =?UTF-8?q?=EC=97=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Buyer/BuyerHome/HomeConsultInProgress.tsx | 3 ++- src/components/Buyer/BuyerHome/HomeConsultInReady.tsx | 11 +++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/components/Buyer/BuyerHome/HomeConsultInProgress.tsx b/src/components/Buyer/BuyerHome/HomeConsultInProgress.tsx index de0ec8a2..621b0cc3 100644 --- a/src/components/Buyer/BuyerHome/HomeConsultInProgress.tsx +++ b/src/components/Buyer/BuyerHome/HomeConsultInProgress.tsx @@ -145,10 +145,11 @@ const Wrapper = styled.div` .nav-consult { display: flex; width: 100%; + height: 4.4rem; box-sizing: border-box; padding: 2.2rem 3.2rem 1.2rem 2rem; - justify-content: space-between; align-items: center; + justify-content: space-between; cursor: pointer; margin-bottom: 0.4rem; } diff --git a/src/components/Buyer/BuyerHome/HomeConsultInReady.tsx b/src/components/Buyer/BuyerHome/HomeConsultInReady.tsx index 577e2e70..6df89ad2 100644 --- a/src/components/Buyer/BuyerHome/HomeConsultInReady.tsx +++ b/src/components/Buyer/BuyerHome/HomeConsultInReady.tsx @@ -64,7 +64,10 @@ const Wrapper = styled.div` width: 100%; height: 4.4rem; display: flex; + box-sizing: border-box; + align-items: center; justify-content: space-between; + padding: 2.2rem 3.2rem 1.2rem 2rem; cursor: pointer; margin-bottom: 0.4rem; } @@ -73,10 +76,6 @@ const NavConsult = styled.div` display: flex; align-items: center; gap: 0.8rem; - margin-top: 0.9rem; - margin-left: 2rem; -`; -const MoreIcon = styled(More)` - margin-right: 3.8rem; - margin-top: 1.5rem; + margin: 0px auto 0px 0px; `; +const MoreIcon = styled(More)``; From 07a82dcb5c9771e7520b0dca59ff9568ba66bbdd Mon Sep 17 00:00:00 2001 From: inyoung Date: Thu, 18 Apr 2024 23:21:17 +0900 Subject: [PATCH 04/18] =?UTF-8?q?Fix:=20=EA=B2=80=EC=83=89=20=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=80=20input=20=EC=8A=A4=ED=83=80=EC=9D=BC=20?= =?UTF-8?q?=EB=A6=AC=ED=8C=A9=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Buyer/BuyerSearch/SearchHeader.tsx | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/components/Buyer/BuyerSearch/SearchHeader.tsx b/src/components/Buyer/BuyerSearch/SearchHeader.tsx index caa5e843..09de255e 100644 --- a/src/components/Buyer/BuyerSearch/SearchHeader.tsx +++ b/src/components/Buyer/BuyerSearch/SearchHeader.tsx @@ -40,8 +40,8 @@ export const SearchHeader = () => { placeHolderColor={Grey4} height="4.4rem" width="100%" - padding="0 3.2rem 0 0" - textIndent="1rem" + isBoxSizing={true} + padding="0.8rem 3.4rem 0.8rem 1.6rem" /> @@ -50,26 +50,25 @@ export const SearchHeader = () => { }; const Wrapper = styled.div` height: 5.2rem; + gap: 0.8rem; background-color: ${White}; position: relative; display: flex; align-items: center; justify-content: center; + box-sizing: border-box; padding: 0.4rem 2rem; `; const BackIcon = styled(Back)` - position: absolute; - top: 1.4rem; - left: 2rem; cursor: pointer; `; const FormWrapper = styled.form` position: relative; - width: 79%; + width: 80%; `; const SearchIcon = styled(Search)` position: absolute; - right: -2.7rem; + right: 0.8rem; top: 0.8rem; cursor: pointer; `; From 7600c19964b98b2648c92a5a3eac56c1f40a6699 Mon Sep 17 00:00:00 2001 From: inyoung Date: Thu, 18 Apr 2024 23:38:43 +0900 Subject: [PATCH 05/18] Fix: revised api url reflection 228 --- src/api/patch.ts | 18 +++++++++++++-- .../Buyer/BuyerSearch/SearchHeader.tsx | 2 +- src/pages/Buyer/BuyerSearchResult.tsx | 23 +++++++++++-------- 3 files changed, 31 insertions(+), 12 deletions(-) diff --git a/src/api/patch.ts b/src/api/patch.ts index a710a214..01c3115f 100644 --- a/src/api/patch.ts +++ b/src/api/patch.ts @@ -64,9 +64,23 @@ export const patchApplyPayments = async (id: any) => //SearchWord Controller //검색 결과 반환 -export const patchSearchWordsResults = async (sortType: string, body: any) => - await patchPublicInstance(`/searchWords/results?sortType=${sortType}`, body); +export const patchSearchWordsCounselorsResults = async ( + sortType: string, + body: any, +) => + await patchPublicInstance( + `/searchWords/results/counselors?sortType=${sortType}`, + body, + ); +export const patchSearchWordsPostsResults = async ( + sortType: string, + body: any, +) => + await patchPublicInstance( + `/searchWords/results/posts?sortType=${sortType}`, + body, + ); //Wishlist Controlloer //찜하기 추가 export const patchWishLists = async (counselorId: number) => diff --git a/src/components/Buyer/BuyerSearch/SearchHeader.tsx b/src/components/Buyer/BuyerSearch/SearchHeader.tsx index 09de255e..b18b73ca 100644 --- a/src/components/Buyer/BuyerSearch/SearchHeader.tsx +++ b/src/components/Buyer/BuyerSearch/SearchHeader.tsx @@ -64,7 +64,7 @@ const BackIcon = styled(Back)` `; const FormWrapper = styled.form` position: relative; - width: 80%; + width: 100%; `; const SearchIcon = styled(Search)` position: absolute; diff --git a/src/pages/Buyer/BuyerSearchResult.tsx b/src/pages/Buyer/BuyerSearchResult.tsx index ad24a046..efa3ef98 100644 --- a/src/pages/Buyer/BuyerSearchResult.tsx +++ b/src/pages/Buyer/BuyerSearchResult.tsx @@ -17,7 +17,10 @@ import { searchKeywordState, } from 'utils/atom'; import Input from 'components/Common/Input'; -import { patchSearchWordsResults } from 'api/patch'; +import { + patchSearchWordsCounselorsResults, + patchSearchWordsResults, +} from 'api/patch'; import { SearchResultData } from 'utils/type'; import { ConverSortType } from 'utils/convertSortType'; import { ReactComponent as Empty } from 'assets/icons/graphic-noting.svg'; @@ -81,7 +84,10 @@ export const BuyerSearchResult = () => { index: pageIndex, }; const sortTypeString: string = ConverSortType(sortType); - const res: any = await patchSearchWordsResults(sortTypeString, body); + const res: any = await patchSearchWordsCounselorsResults( + sortTypeString, + body, + ); if (res.status === 200) { if (res.data.length !== 0) { if (pageIndex === 0) { @@ -155,8 +161,8 @@ export const BuyerSearchResult = () => { placeHolderColor={Grey4} height="4.4rem" width="100%" - padding="0 3.2rem 0 0" - textIndent="1rem" + isBoxSizing={true} + padding="0.8rem 3.4rem 0.8rem 1.6rem" /> @@ -230,26 +236,25 @@ const Wrapper = styled.div` `; const HeaderWrapper = styled.div` height: 5.2rem; + gap: 0.8rem; background-color: ${White}; position: relative; display: flex; align-items: center; justify-content: center; + box-sizing: border-box; padding: 0.4rem 2rem; `; const FormWrapper = styled.form` position: relative; - width: 79%; + width: 100%; `; const BackIcon = styled(Back)` - position: absolute; - top: 1.4rem; - left: 2rem; cursor: pointer; `; const SearchIcon = styled(Search)` position: absolute; - right: -2.7rem; + right: 0.8rem; top: 0.8rem; cursor: pointer; `; From 1915ae71aaf04b845560b67722915d686df5cd01 Mon Sep 17 00:00:00 2001 From: inyoung Date: Fri, 19 Apr 2024 00:44:57 +0900 Subject: [PATCH 06/18] =?UTF-8?q?Feat:=20=EA=B2=80=EC=83=89=20=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=80=20api=20=EC=97=B0=EB=8F=99=20=EB=B0=8F=20?= =?UTF-8?q?=EC=97=AC=EB=9F=AC=20=ED=95=84=ED=84=B0=EB=A7=81=EC=97=90=20?= =?UTF-8?q?=EB=94=B0=EB=A5=B8=20=EB=B7=B0=20=EA=B5=AC=ED=98=84=20#228?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../BuyerSearchResult/OpenConsultResults.tsx | 25 ++ .../Buyer/Common/OpenConsultSortModal.tsx | 136 ++++++++ src/components/Common/Divider2.tsx | 2 - src/pages/Buyer/BuyerSearchResult.tsx | 313 +++++++++++++----- src/utils/constant.ts | 1 + src/utils/convertOpenSortType.ts | 11 + 6 files changed, 405 insertions(+), 83 deletions(-) create mode 100644 src/components/Buyer/BuyerSearchResult/OpenConsultResults.tsx create mode 100644 src/components/Buyer/Common/OpenConsultSortModal.tsx create mode 100644 src/utils/convertOpenSortType.ts diff --git a/src/components/Buyer/BuyerSearchResult/OpenConsultResults.tsx b/src/components/Buyer/BuyerSearchResult/OpenConsultResults.tsx new file mode 100644 index 00000000..6e3e1c51 --- /dev/null +++ b/src/components/Buyer/BuyerSearchResult/OpenConsultResults.tsx @@ -0,0 +1,25 @@ +import React from 'react'; +import styled from 'styled-components'; +import { openConsultApiObject } from 'pages/Buyer/BuyerConsult'; +import SavedOpenConsultCard from '../BuyerSavedCounselor.tsx/SavedOpenConsultCard'; +interface OpenConsultResultProps { + openConsultList: openConsultApiObject[]; +} +function OpenConsultResults({ openConsultList }: OpenConsultResultProps) { + return ( + + {openConsultList.map((item) => ( + + ))} + + ); +} + +const Wrapper = styled.div` + display: flex; + flex-direction: column; + gap: 1.2rem; + align-items: center; + width: 100%; +`; +export default OpenConsultResults; diff --git a/src/components/Buyer/Common/OpenConsultSortModal.tsx b/src/components/Buyer/Common/OpenConsultSortModal.tsx new file mode 100644 index 00000000..ee39bdce --- /dev/null +++ b/src/components/Buyer/Common/OpenConsultSortModal.tsx @@ -0,0 +1,136 @@ +import { ReactComponent as CheckIcon } from 'assets/icons/icon-modal-check.svg'; +import { SetStateAction } from 'react'; +import { useRecoilState, useSetRecoilState } from 'recoil'; +import styled, { keyframes } from 'styled-components'; +import { Green, Grey1, Grey4, Grey6 } from 'styles/color'; +import { Body1 } from 'styles/font'; +import { isSortModalOpenState, scrollLockState } from 'utils/atom'; +interface SortModalProps { + sortType: number; + setSortType: React.Dispatch>; + setPostId: React.Dispatch>; +} +//최근순 인기순 별점순 모달 +export const OpenConsultSortModal = ({ + sortType, + setSortType, + setPostId, +}: SortModalProps) => { + //modal 여부 + const [isModalOpen, setIsModalOpen] = useRecoilState(isSortModalOpenState); + //scorll 막기 + const setScrollLock = useSetRecoilState(scrollLockState); + + return ( + +
+ +
+
{ + setSortType(0); + setPostId(0); + setIsModalOpen(false); + setScrollLock(false); + }} + > + {sortType === 0 ? ( + <> + 최근순 + + + ) : ( + 최근순 + )} +
+
{ + setSortType(1); + setPostId(0); + setIsModalOpen(false); + setScrollLock(false); + }} + > + {sortType === 1 ? ( + <> + 공감 많은 순 + + + ) : ( + 공감 많은 순 + )} +
+
{ + setSortType(2); + setPostId(0); + setIsModalOpen(false); + setScrollLock(false); + }} + > + {sortType === 2 ? ( + <> + 댓글 많은 순 + + + ) : ( + 댓글 많은 순 + )} +
+
+ ); +}; +const slideIn = keyframes` + from{ + transform : translateY(100%); + } + to{ + transform : translateY(0%); + } +`; +const slideOut = keyframes` + from{ + transform : translateY(0%); + } + to{ + transform : translateY(100%); + } +`; +const Wrapper = styled.div<{ visible: boolean }>` + @media (max-width: 767px) { + width: 100vw; + } + @media (min-width: 768px) { + width: 37.5rem; + } + position: fixed; + height: 22.7rem; + background-color: ${Grey6}; + bottom: 0; + border-radius: 2rem 2rem 0 0; + box-shadow: 0px -4px 10px rgba(0, 0, 0, 0.1); + z-index: 2002; + animation: ${({ visible }) => (visible ? slideIn : slideOut)} 0.3s ease-in-out; + + .bar-wrapper { + height: 4.5rem; + display: flex; + justify-content: center; + } + .row { + display: flex; + padding: 1rem 2rem 0 2rem; + height: 4.4rem; + justify-content: space-between; + cursor: pointer; + } +`; +const Bar = styled.div` + margin-top: 1.2rem; + width: 3.1rem; + height: 0.3rem; + background-color: ${Grey4}; +`; diff --git a/src/components/Common/Divider2.tsx b/src/components/Common/Divider2.tsx index 06f84e8b..70ed5297 100644 --- a/src/components/Common/Divider2.tsx +++ b/src/components/Common/Divider2.tsx @@ -53,8 +53,6 @@ const Wrapper = styled.nav` display: flex; justify-content: center; border-bottom: 1px solid ${Grey6}; - position: sticky; - top: 5.3rem; background-color: white; z-index: 999; `; diff --git a/src/pages/Buyer/BuyerSearchResult.tsx b/src/pages/Buyer/BuyerSearchResult.tsx index efa3ef98..6a301c26 100644 --- a/src/pages/Buyer/BuyerSearchResult.tsx +++ b/src/pages/Buyer/BuyerSearchResult.tsx @@ -8,10 +8,9 @@ import { ReactComponent as Down } from 'assets/icons/icon-drop-down.svg'; import { SearchResults } from 'components/Buyer/BuyerSearchResult/SearchResults'; import { SortModal } from 'components/Buyer/Common/SortModal'; import { ChangeEvent, useLayoutEffect, useRef, useState } from 'react'; -import { sortList } from 'utils/constant'; +import { openSortList, sortList } from 'utils/constant'; import { useRecoilState, useSetRecoilState } from 'recoil'; import { - isLoadingState, isSortModalOpenState, scrollLockState, searchKeywordState, @@ -19,13 +18,18 @@ import { import Input from 'components/Common/Input'; import { patchSearchWordsCounselorsResults, - patchSearchWordsResults, + patchSearchWordsPostsResults, } from 'api/patch'; import { SearchResultData } from 'utils/type'; import { ConverSortType } from 'utils/convertSortType'; import { ReactComponent as Empty } from 'assets/icons/graphic-noting.svg'; import { LoadingSpinner } from 'utils/LoadingSpinner'; import useIntersectionObserver from 'hooks/useIntersectionObserver'; +import Divider2 from 'components/Common/Divider2'; +import OpenConsultResults from 'components/Buyer/BuyerSearchResult/OpenConsultResults'; +import { openConsultApiObject } from './BuyerConsult'; +import { OpenConsultSortModal } from 'components/Buyer/Common/OpenConsultSortModal'; +import { ConverOpenSortType } from 'utils/convertOpenSortType'; export const BuyerSearchResult = () => { const navigate = useNavigate(); //0 : 최신순 1:인기순 2: 별점순 @@ -43,8 +47,14 @@ export const BuyerSearchResult = () => { const [input, setInput] = useState(initInput); //결과저장 const [searchData, setSearchData] = useState([]); + const [openConsultSearchData, setOpenConsultSearchData] = useState< + openConsultApiObject[] + >([]); //무한스크롤 위한 page num const [pageNum, setPageNum] = useState(0); + const [lastId, setLastId] = useState(0); + // 상담사 탭 0, 공개 상담 1 + const [tabState, setTabState] = useState(1); const [isLastElem, setIsLastElem] = useState(false); const preventRef = useRef(true); // 중복 방지 옵션 const onIntersect: IntersectionObserverCallback = async (entry) => { @@ -55,7 +65,12 @@ export const BuyerSearchResult = () => { preventRef.current ) { preventRef.current = false; - await fetchSearchResults(keyword, pageNum); + if (tabState === 1) { + await fetchSearchResults(keyword, pageNum); + } else if (tabState === 2) { + await fetchOpenSearchResults(keyword, lastId); + } + preventRef.current = true; } }; @@ -112,13 +127,51 @@ export const BuyerSearchResult = () => { } } }; + + const fetchOpenSearchResults = async (searchWord: string, postId: number) => { + try { + const body = { + word: searchWord, + postId: postId, + }; + const sortTypeString: string = ConverOpenSortType(sortType); + const res: any = await patchSearchWordsPostsResults(sortTypeString, body); + if (res.status === 200) { + if (res.data.length !== 0) { + if (postId === 0) { + setOpenConsultSearchData(res.data); + setPageNum(pageNum + 1); + } else { + const updatedSearchs = [...openConsultSearchData, ...res.data]; + setLastId(res.data.postId); + setOpenConsultSearchData(updatedSearchs); + } + setIsLastElem(true); + } else { + setOpenConsultSearchData([]); + } + } else if (res.response.status === 400) { + alert('검색어는 2~20자 사이여야 합니다.'); + } + } catch (e) { + alert(e); + } finally { + if (postId === 0) { + setIsLoading(false); + } + } + }; useLayoutEffect(() => { setIsLastElem(false); setPageNum(0); setSearchData([]); setIsLoading(true); - fetchSearchResults(keyword, 0); - }, [keyword, sortType]); + if (tabState === 1) { + fetchSearchResults(keyword, 0); + } else if (tabState === 2) { + fetchOpenSearchResults(keyword, 0); + } + }, [keyword, sortType, tabState]); if (isLoading) { return ( <> @@ -142,105 +195,195 @@ export const BuyerSearchResult = () => { ); } else { - return ( - - - { - navigate('/search'); - }} - /> - - - - - -
-
{ - setIsModalOpen(true); - setScrollLock(true); - }} - > - {sortList[sortType]} - -
-
- {searchData.length !== 0 ? ( - <> - - {!isLastElem ? ( -
- ) : ( -
- )} - - ) : ( - - - - 검색 결과가 없어요. - - 마인더명, 제목, 상담 스타일 등 - 더 간단한 단어로 검색해보세요. - - )} + if (tabState === 1) { + return ( + + + + { + navigate('/search'); + }} + /> + + + + + + +
+
{ + setIsModalOpen(true); + setScrollLock(true); + }} + > + {sortList[sortType]} + +
+
+
- {isModalOpen ? ( - <> - { - //여기서 api 호출 - setIsModalOpen(false); - setScrollLock(false); - }} - /> - - - ) : null} -
- ); + {searchData.length !== 0 ? ( + <> + + {!isLastElem ? ( +
+ ) : ( +
+ )} + + ) : ( + + + + 검색 결과가 없어요. + + 마인더명, 제목, 상담 스타일 등 + 더 간단한 단어로 검색해보세요. + + )} + + {isModalOpen ? ( + <> + { + //여기서 api 호출 + setIsModalOpen(false); + setScrollLock(false); + }} + /> + + + ) : null} + + ); + } else if (tabState === 2) { + return ( + + + + { + navigate('/search'); + }} + /> + + + + + + +
+
{ + setIsModalOpen(true); + setScrollLock(true); + }} + > + {openSortList[sortType]} + +
+
+
+ + {openConsultSearchData.length !== 0 ? ( + <> + + {!isLastElem ? ( +
+ ) : ( +
+ )} + + ) : ( + + + + 검색 결과가 없어요. + + 마인더명, 제목, 상담 스타일 등 + 더 간단한 단어로 검색해보세요. + + )} + + {isModalOpen ? ( + <> + { + //여기서 api 호출 + setIsModalOpen(false); + setScrollLock(false); + }} + /> + + + ) : null} + + ); + } } }; const Wrapper = styled.div` .select { display: flex; + background-color: white; height: 4.4rem; padding: 0.4rem 2rem; align-items: center; justify-content: flex-end; cursor: pointer; + z-index: 9; } .select-wrapper { display: flex; gap: 0.4rem; } `; -const HeaderWrapper = styled.div` +const HeaderWrapper = styled.header` height: 5.2rem; gap: 0.8rem; background-color: ${White}; position: relative; display: flex; align-items: center; + top: 0; + width: 100%; justify-content: center; box-sizing: border-box; padding: 0.4rem 2rem; @@ -281,3 +424,11 @@ const EmptyWrapper = styled.div` flex-direction: column; align-items: center; `; + +const FixedContainer = styled.section` + position: sticky; + width: 100%; + top: 0; + background-color: white; + z-index: 99; +`; diff --git a/src/utils/constant.ts b/src/utils/constant.ts index 909cea7b..0513e658 100644 --- a/src/utils/constant.ts +++ b/src/utils/constant.ts @@ -13,6 +13,7 @@ export const categories = [ '조언', '팩폭', ]; +export const openSortList = ['최근순', '공감 많은 순', '댓글 많은 순']; export const sortList = ['최근순', '인기순', '별점순']; export const quizList = [ diff --git a/src/utils/convertOpenSortType.ts b/src/utils/convertOpenSortType.ts new file mode 100644 index 00000000..c8e82a2a --- /dev/null +++ b/src/utils/convertOpenSortType.ts @@ -0,0 +1,11 @@ +export const ConverOpenSortType = (typeNum: number) => { + if (typeNum === 0) { + return 'LATEST'; + } else if (typeNum === 1) { + return 'DESC_TOTAL_COMMENT'; + } else if (typeNum === 2) { + return 'DESC_TOTAL_LIKE'; + } else { + return ''; + } +}; From 93f467d5e282bf5452017f936d6a953425736bbe Mon Sep 17 00:00:00 2001 From: inyoung Date: Fri, 19 Apr 2024 00:50:07 +0900 Subject: [PATCH 07/18] Fix: add input component in loading ui #228 --- src/pages/Buyer/BuyerSearchResult.tsx | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/pages/Buyer/BuyerSearchResult.tsx b/src/pages/Buyer/BuyerSearchResult.tsx index 6a301c26..72e15275 100644 --- a/src/pages/Buyer/BuyerSearchResult.tsx +++ b/src/pages/Buyer/BuyerSearchResult.tsx @@ -181,7 +181,24 @@ export const BuyerSearchResult = () => { navigate('/consult'); }} /> + + + + +
Date: Fri, 19 Apr 2024 21:31:08 +0900 Subject: [PATCH 08/18] =?UTF-8?q?Feat:=20=EA=B2=80=EC=83=89=20=ED=95=84?= =?UTF-8?q?=ED=84=B0=EB=A7=81=20=EC=A1=B0=EA=B1=B4=20=EB=B0=8F=20=EA=B2=80?= =?UTF-8?q?=EC=83=89=EC=96=B4=20=EC=BF=BC=EB=A6=AC=20=EC=8A=A4=ED=8A=B8?= =?UTF-8?q?=EB=A7=81=20=EA=B0=92=EC=9C=BC=EB=A1=9C=20=EB=B0=98=EC=98=81?= =?UTF-8?q?=ED=95=98=EC=97=AC=20=EA=B2=80=EC=83=89=20=EA=B2=B0=EA=B3=BC=20?= =?UTF-8?q?=EC=9C=A0=EC=A7=80,=20useSearchPageParams=20=EA=B5=AC=ED=98=84?= =?UTF-8?q?=20#233?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Buyer/Common/OpenConsultSortModal.tsx | 11 +++ src/components/Buyer/Common/SortModal.tsx | 11 +++ src/components/Common/Divider2.tsx | 2 +- src/hooks/useSearchPageParams.ts | 78 +++++++++++++++++++ src/pages/Buyer/BuyerSearchResult.tsx | 76 +++++++++--------- src/utils/convertOpenSortType.ts | 4 +- 6 files changed, 141 insertions(+), 41 deletions(-) create mode 100644 src/hooks/useSearchPageParams.ts diff --git a/src/components/Buyer/Common/OpenConsultSortModal.tsx b/src/components/Buyer/Common/OpenConsultSortModal.tsx index ee39bdce..bab47d48 100644 --- a/src/components/Buyer/Common/OpenConsultSortModal.tsx +++ b/src/components/Buyer/Common/OpenConsultSortModal.tsx @@ -1,5 +1,6 @@ import { ReactComponent as CheckIcon } from 'assets/icons/icon-modal-check.svg'; import { SetStateAction } from 'react'; +import { SetURLSearchParams } from 'react-router-dom'; import { useRecoilState, useSetRecoilState } from 'recoil'; import styled, { keyframes } from 'styled-components'; import { Green, Grey1, Grey4, Grey6 } from 'styles/color'; @@ -9,12 +10,16 @@ interface SortModalProps { sortType: number; setSortType: React.Dispatch>; setPostId: React.Dispatch>; + searchParams: URLSearchParams; + setSearchParams: SetURLSearchParams; } //최근순 인기순 별점순 모달 export const OpenConsultSortModal = ({ sortType, setSortType, setPostId, + searchParams, + setSearchParams, }: SortModalProps) => { //modal 여부 const [isModalOpen, setIsModalOpen] = useRecoilState(isSortModalOpenState); @@ -31,6 +36,8 @@ export const OpenConsultSortModal = ({ onClick={() => { setSortType(0); setPostId(0); + searchParams.set('open-sort', 'recent'); + setSearchParams(searchParams); setIsModalOpen(false); setScrollLock(false); }} @@ -49,6 +56,8 @@ export const OpenConsultSortModal = ({ onClick={() => { setSortType(1); setPostId(0); + searchParams.set('open-sort', 'likes'); + setSearchParams(searchParams); setIsModalOpen(false); setScrollLock(false); }} @@ -67,6 +76,8 @@ export const OpenConsultSortModal = ({ onClick={() => { setSortType(2); setPostId(0); + searchParams.set('open-sort', 'comments'); + setSearchParams(searchParams); setIsModalOpen(false); setScrollLock(false); }} diff --git a/src/components/Buyer/Common/SortModal.tsx b/src/components/Buyer/Common/SortModal.tsx index 4837cc22..b53d5f06 100644 --- a/src/components/Buyer/Common/SortModal.tsx +++ b/src/components/Buyer/Common/SortModal.tsx @@ -1,5 +1,6 @@ import { ReactComponent as CheckIcon } from 'assets/icons/icon-modal-check.svg'; import { SetStateAction } from 'react'; +import { SetURLSearchParams } from 'react-router-dom'; import { useRecoilState, useSetRecoilState } from 'recoil'; import styled, { keyframes } from 'styled-components'; import { Green, Grey1, Grey4, Grey6 } from 'styles/color'; @@ -9,12 +10,16 @@ interface SortModalProps { sortType: number; setSortType: React.Dispatch>; setPageNum: React.Dispatch>; + searchParams: URLSearchParams; + setSearchParams: SetURLSearchParams; } //최근순 인기순 별점순 모달 export const SortModal = ({ sortType, setSortType, setPageNum, + searchParams, + setSearchParams, }: SortModalProps) => { //modal 여부 const [isModalOpen, setIsModalOpen] = useRecoilState(isSortModalOpenState); @@ -32,6 +37,8 @@ export const SortModal = ({ setSortType(0); setPageNum(0); setIsModalOpen(false); + searchParams.set('sort', 'recent'); + setSearchParams(searchParams); setScrollLock(false); }} > @@ -49,6 +56,8 @@ export const SortModal = ({ onClick={() => { setSortType(1); setPageNum(0); + searchParams.set('sort', 'popular'); + setSearchParams(searchParams); setIsModalOpen(false); setScrollLock(false); }} @@ -67,6 +76,8 @@ export const SortModal = ({ onClick={() => { setSortType(2); setPageNum(0); + searchParams.set('sort', 'rating'); + setSearchParams(searchParams); setIsModalOpen(false); setScrollLock(false); }} diff --git a/src/components/Common/Divider2.tsx b/src/components/Common/Divider2.tsx index 70ed5297..b2b69a26 100644 --- a/src/components/Common/Divider2.tsx +++ b/src/components/Common/Divider2.tsx @@ -6,7 +6,7 @@ import { ReactComponent as UnderLineBuyerBig } from 'assets/icons/underline-big. import { Subtitle } from 'styles/font'; interface Divder2Props { tabState: number; - setTabState: React.Dispatch>; + setTabState: (tabState: number) => void; } function Divider2({ tabState, setTabState }: Divder2Props) { return ( diff --git a/src/hooks/useSearchPageParams.ts b/src/hooks/useSearchPageParams.ts new file mode 100644 index 00000000..8bced2a6 --- /dev/null +++ b/src/hooks/useSearchPageParams.ts @@ -0,0 +1,78 @@ +import { ChangeEvent, useCallback, useState } from 'react'; +import { useSearchParams } from 'react-router-dom'; +import { useRecoilValue } from 'recoil'; +import { searchKeywordState } from 'utils/atom'; + +export const useSearchPageParams = () => { + const [searchParams, setSearchParams] = useSearchParams(); + const keywordParam = searchParams.get('keyword'); + const typeParam = searchParams.get('searchType'); + const sortParam = searchParams.get('sort'); + const openSortParam = searchParams.get('open-sort'); + const keywordFromPrevPage = useRecoilValue(searchKeywordState); + const initialKeyword = keywordParam ?? keywordFromPrevPage; + const initialSearchType = typeParam ?? 'counselor'; + const initialSortType = + sortParam === 'rating' ? 2 : sortParam === 'popular' ? 1 : 0; + const initialOpenSortType = + openSortParam === 'comments' ? 2 : openSortParam === 'likes' ? 1 : 0; + const initialTabState = typeParam === 'open-consult' ? 2 : 1; + const [input, setInput] = useState(keywordFromPrevPage); + const [keyword, setKeyword] = useState(initialKeyword); + const [searchType, setSearchType] = useState(initialSearchType); + const [openSortType, setOpenSortType] = useState(initialOpenSortType); + const [sortType, setSortType] = useState(initialSortType); + const [tabState, setTabState] = useState(initialTabState); + const handleClickOpenConsult = useCallback(() => { + setSearchType('open-consult'); + searchParams.set('searchType', 'open-consult'); + setTabState(2); + setSearchParams(searchParams); + }, []); + const handleClickCounselor = useCallback(() => { + setSearchType('counselor'); + searchParams.set('searchType', 'counselor'); + setSearchParams(searchParams); + setTabState(1); + console.log('hi'); + }, []); + + const handleClickTab = useCallback( + (tabState: number) => { + if (tabState === 1) { + handleClickCounselor(); + } else { + handleClickOpenConsult(); + } + }, + [tabState], + ); + const handleChangeInput = (event: ChangeEvent) => { + setInput(event.target.value); + }; + const handleSubmit = (event: React.FormEvent) => { + event.preventDefault(); + setKeyword(input); + searchParams.set('keyword', input); + setSearchParams(searchParams); + }; + + return { + handleClickTab, + searchParams, + openSortType, + setOpenSortType, + setSearchParams, + input, + tabState, + setTabState, + keyword, + setKeyword, + handleSubmit, + keywordFromPrevPage, + searchType, + sortType, + setSortType, + handleChangeInput, + }; +}; diff --git a/src/pages/Buyer/BuyerSearchResult.tsx b/src/pages/Buyer/BuyerSearchResult.tsx index 72e15275..35c60ded 100644 --- a/src/pages/Buyer/BuyerSearchResult.tsx +++ b/src/pages/Buyer/BuyerSearchResult.tsx @@ -7,13 +7,12 @@ import { ReactComponent as Search } from 'assets/icons/search.svg'; import { ReactComponent as Down } from 'assets/icons/icon-drop-down.svg'; import { SearchResults } from 'components/Buyer/BuyerSearchResult/SearchResults'; import { SortModal } from 'components/Buyer/Common/SortModal'; -import { ChangeEvent, useLayoutEffect, useRef, useState } from 'react'; +import { useLayoutEffect, useRef, useState } from 'react'; import { openSortList, sortList } from 'utils/constant'; import { useRecoilState, useSetRecoilState } from 'recoil'; import { isSortModalOpenState, scrollLockState, - searchKeywordState, } from 'utils/atom'; import Input from 'components/Common/Input'; import { @@ -30,33 +29,40 @@ import OpenConsultResults from 'components/Buyer/BuyerSearchResult/OpenConsultRe import { openConsultApiObject } from './BuyerConsult'; import { OpenConsultSortModal } from 'components/Buyer/Common/OpenConsultSortModal'; import { ConverOpenSortType } from 'utils/convertOpenSortType'; +import { useSearchPageParams } from 'hooks/useSearchPageParams'; export const BuyerSearchResult = () => { const navigate = useNavigate(); - //0 : 최신순 1:인기순 2: 별점순 - // 바뀔 때마다 useEffect로 request - const [sortType, setSortType] = useState(0); - // Modal 여부(recoil) + // Modal 여부(recoil), 스크롤 막기 const [isModalOpen, setIsModalOpen] = useRecoilState(isSortModalOpenState); - //scorll 막기 const setScrollLock = useSetRecoilState(scrollLockState); - //검색된 value - const [keyword, setKeyword] = useRecoilState(searchKeywordState); - //input value - const initInput = keyword; - const [input, setInput] = useState(initInput); - //결과저장 + // 검색 결과 리스트 - 상담사 리스트와 공개 상담 리스트 const [searchData, setSearchData] = useState([]); const [openConsultSearchData, setOpenConsultSearchData] = useState< openConsultApiObject[] >([]); - //무한스크롤 위한 page num + //무한스크롤 위한 page num과 lastId const [pageNum, setPageNum] = useState(0); const [lastId, setLastId] = useState(0); - // 상담사 탭 0, 공개 상담 1 - const [tabState, setTabState] = useState(1); + // 무한스크롤을 trigger를 위한 상태 const [isLastElem, setIsLastElem] = useState(false); - const preventRef = useRef(true); // 중복 방지 옵션 + // 무한스크롤을 trigger를 위한 상태 + const [isLoading, setIsLoading] = useState(true); + const preventRef = useRef(true); + const { + handleClickTab, + searchParams, + setSearchParams, + openSortType, + setOpenSortType, + input, + keyword, + tabState, + handleSubmit, + sortType, + setSortType, + handleChangeInput, + } = useSearchPageParams(); const onIntersect: IntersectionObserverCallback = async (entry) => { if ( entry[0].isIntersecting && @@ -74,24 +80,13 @@ export const BuyerSearchResult = () => { preventRef.current = true; } }; - //현재 대상 및 option을 props로 전달 const { setTarget } = useIntersectionObserver({ root: null, rootMargin: '0px', threshold: 0.8, onIntersect, }); - //input onchagne - const handleOnChange = (event: ChangeEvent) => { - setInput(event.target.value); - }; - const handleSubmit: any = (event: ChangeEvent) => { - event.preventDefault(); - setKeyword(input); - }; - //로딩 state - const [isLoading, setIsLoading] = useState(true); const fetchSearchResults = async (searchWord: string, pageIndex: number) => { try { const body = { @@ -134,7 +129,7 @@ export const BuyerSearchResult = () => { word: searchWord, postId: postId, }; - const sortTypeString: string = ConverOpenSortType(sortType); + const sortTypeString: string = ConverOpenSortType(openSortType); const res: any = await patchSearchWordsPostsResults(sortTypeString, body); if (res.status === 200) { if (res.data.length !== 0) { @@ -171,7 +166,8 @@ export const BuyerSearchResult = () => { } else if (tabState === 2) { fetchOpenSearchResults(keyword, 0); } - }, [keyword, sortType, tabState]); + }, [keyword, sortType, tabState, openSortType]); + if (isLoading) { return ( <> @@ -184,7 +180,7 @@ export const BuyerSearchResult = () => { { { - +
{ sortType={sortType} setSortType={setSortType} setPageNum={setPageNum} + searchParams={searchParams} + setSearchParams={setSearchParams} /> ) : null} @@ -305,7 +303,7 @@ export const BuyerSearchResult = () => { { - +
{ setScrollLock(true); }} > - {openSortList[sortType]} + {openSortList[openSortType]}
@@ -364,9 +362,11 @@ export const BuyerSearchResult = () => { }} /> ) : null} diff --git a/src/utils/convertOpenSortType.ts b/src/utils/convertOpenSortType.ts index c8e82a2a..27429624 100644 --- a/src/utils/convertOpenSortType.ts +++ b/src/utils/convertOpenSortType.ts @@ -2,9 +2,9 @@ export const ConverOpenSortType = (typeNum: number) => { if (typeNum === 0) { return 'LATEST'; } else if (typeNum === 1) { - return 'DESC_TOTAL_COMMENT'; - } else if (typeNum === 2) { return 'DESC_TOTAL_LIKE'; + } else if (typeNum === 2) { + return 'DESC_TOTAL_COMMENT'; } else { return ''; } From 22076bc030f48d63a981dc135b7b217b4d10b6c6 Mon Sep 17 00:00:00 2001 From: inyoung Date: Sun, 21 Apr 2024 00:50:22 +0900 Subject: [PATCH 09/18] Chore: Add comments to variables and functions and remove unused imports, add dependecny to useEffect #228 --- .../SavedCounselorCard.tsx | 28 ++- src/components/Common/Divider2.tsx | 13 +- src/components/Common/TagA2Cartegory.tsx | 10 +- src/hooks/useSearchPageParams.ts | 45 +++-- src/pages/Buyer/BuyerHome.tsx | 35 ++-- src/pages/Buyer/BuyerSearchResult.tsx | 159 ++++++++++-------- src/utils/convertOpenSortType.ts | 2 +- 7 files changed, 177 insertions(+), 115 deletions(-) diff --git a/src/components/Buyer/BuyerSavedCounselor.tsx/SavedCounselorCard.tsx b/src/components/Buyer/BuyerSavedCounselor.tsx/SavedCounselorCard.tsx index 0e0f0754..ff6e5262 100644 --- a/src/components/Buyer/BuyerSavedCounselor.tsx/SavedCounselorCard.tsx +++ b/src/components/Buyer/BuyerSavedCounselor.tsx/SavedCounselorCard.tsx @@ -8,11 +8,16 @@ import { ReactComponent as NoneBookMark } from 'assets/icons/icon-save1.svg'; import { ReactComponent as BookMark } from 'assets/icons/icon-save2.svg'; import { ReactComponent as DownIcon } from 'assets/icons/icon-down-toggle.svg'; import { ReactComponent as UpIcon } from 'assets/icons/icon-up-toggle.svg'; -import { useEffect, useState } from 'react'; +import { useState } from 'react'; import { useNavigate } from 'react-router-dom'; import { CartegoryState, ConsultTimes } from 'utils/type'; import { convertTimeToString } from 'utils/convertTimeToString'; import { deleteWishLists } from 'api/delete'; + +// +// +// + interface SavedCounselorCardProps { counselorId: number; tagList: CartegoryState[]; @@ -28,7 +33,11 @@ interface SavedCounselorCardProps { chattingPrice: number; consultStyle: number; } -//일단 toggle파트 제외하고 클릭 시 상담프로필로 navigate하게 구현 + +// +// +// + export const SavedCounselorCard = ({ counselorId, tagList, @@ -45,11 +54,12 @@ export const SavedCounselorCard = ({ consultStyle, }: SavedCounselorCardProps) => { const navigate = useNavigate(); - //toggle const [toggle, setToggle] = useState(false); const [isSending, setIsSending] = useState(false); const [isSaved, setIsSaved] = useState(true); - const handleBookmark = async (e: React.MouseEvent) => { + const handleBookmark = async ( + e: React.MouseEvent | React.MouseEvent, + ) => { e.stopPropagation(); if (isSending) { return; @@ -73,19 +83,21 @@ export const SavedCounselorCard = ({ setIsSaved(false); } }; + // + // + // if (isSaved) { return ( { - //마인더 프로필 개발되면 수정 navigate(`/profile/${counselorId}`); }} > - {tagList.map((value: any, index) => { + {tagList.map((value: CartegoryState) => { return ( - + ); })} @@ -116,7 +128,7 @@ export const SavedCounselorCard = ({ ) : ( ) => { + onClick={(e: React.MouseEvent) => { e.stopPropagation(); setIsSaved(true); }} diff --git a/src/components/Common/Divider2.tsx b/src/components/Common/Divider2.tsx index b2b69a26..f55820b0 100644 --- a/src/components/Common/Divider2.tsx +++ b/src/components/Common/Divider2.tsx @@ -1,13 +1,22 @@ -import React, { useState } from 'react'; import styled from 'styled-components'; -import { Black, Green, Grey4, Grey6 } from 'styles/color'; +import { Green, Grey4, Grey6 } from 'styles/color'; import { ReactComponent as UnderLineBuyer } from 'assets/icons/underline-buyer.svg'; import { ReactComponent as UnderLineBuyerBig } from 'assets/icons/underline-big.svg'; import { Subtitle } from 'styles/font'; + +// +// +// + interface Divder2Props { tabState: number; setTabState: (tabState: number) => void; } + +// +// +// + function Divider2({ tabState, setTabState }: Divder2Props) { return ( diff --git a/src/components/Common/TagA2Cartegory.tsx b/src/components/Common/TagA2Cartegory.tsx index e2816598..215cc540 100644 --- a/src/components/Common/TagA2Cartegory.tsx +++ b/src/components/Common/TagA2Cartegory.tsx @@ -2,12 +2,20 @@ import styled from 'styled-components'; import { Green, Grey6, Red, White } from 'styles/color'; import { Caption2 } from 'styles/font'; import { CartegoryState } from 'utils/type'; -//bgcolor type이 1이면 white 2면 해당 색상, 3이면 grey6 + +// +// +// + interface TagA2CartegoryProps { tagType: CartegoryState; bgColorType: number; } +// +// +// + export const TagA2Cartegory = ({ tagType, bgColorType, diff --git a/src/hooks/useSearchPageParams.ts b/src/hooks/useSearchPageParams.ts index 8bced2a6..9034a6aa 100644 --- a/src/hooks/useSearchPageParams.ts +++ b/src/hooks/useSearchPageParams.ts @@ -3,12 +3,20 @@ import { useSearchParams } from 'react-router-dom'; import { useRecoilValue } from 'recoil'; import { searchKeywordState } from 'utils/atom'; +// +// +// + export const useSearchPageParams = () => { const [searchParams, setSearchParams] = useSearchParams(); + + /** get query-string */ const keywordParam = searchParams.get('keyword'); const typeParam = searchParams.get('searchType'); const sortParam = searchParams.get('sort'); const openSortParam = searchParams.get('open-sort'); + + /** get initial values */ const keywordFromPrevPage = useRecoilValue(searchKeywordState); const initialKeyword = keywordParam ?? keywordFromPrevPage; const initialSearchType = typeParam ?? 'counselor'; @@ -17,25 +25,28 @@ export const useSearchPageParams = () => { const initialOpenSortType = openSortParam === 'comments' ? 2 : openSortParam === 'likes' ? 1 : 0; const initialTabState = typeParam === 'open-consult' ? 2 : 1; + + /** states with iniitial value */ const [input, setInput] = useState(keywordFromPrevPage); const [keyword, setKeyword] = useState(initialKeyword); const [searchType, setSearchType] = useState(initialSearchType); const [openSortType, setOpenSortType] = useState(initialOpenSortType); const [sortType, setSortType] = useState(initialSortType); const [tabState, setTabState] = useState(initialTabState); + + /** event handler*/ const handleClickOpenConsult = useCallback(() => { setSearchType('open-consult'); searchParams.set('searchType', 'open-consult'); setTabState(2); setSearchParams(searchParams); - }, []); + }, [searchParams, setSearchParams]); const handleClickCounselor = useCallback(() => { setSearchType('counselor'); searchParams.set('searchType', 'counselor'); setSearchParams(searchParams); setTabState(1); - console.log('hi'); - }, []); + }, [searchParams, setSearchParams]); const handleClickTab = useCallback( (tabState: number) => { @@ -45,17 +56,25 @@ export const useSearchPageParams = () => { handleClickOpenConsult(); } }, - [tabState], + [handleClickCounselor, handleClickOpenConsult], + ); + const handleChangeInput = useCallback( + (event: ChangeEvent) => { + setInput(event.target.value); + }, + [], + ); + const handleSubmit = useCallback( + ( + event: React.FormEvent | React.MouseEvent, + ) => { + event.preventDefault(); + setKeyword(input); + searchParams.set('keyword', input); + setSearchParams(searchParams); + }, + [input, searchParams, setSearchParams], ); - const handleChangeInput = (event: ChangeEvent) => { - setInput(event.target.value); - }; - const handleSubmit = (event: React.FormEvent) => { - event.preventDefault(); - setKeyword(input); - searchParams.set('keyword', input); - setSearchParams(searchParams); - }; return { handleClickTab, diff --git a/src/pages/Buyer/BuyerHome.tsx b/src/pages/Buyer/BuyerHome.tsx index 47051b72..a7a4a3a7 100644 --- a/src/pages/Buyer/BuyerHome.tsx +++ b/src/pages/Buyer/BuyerHome.tsx @@ -11,27 +11,26 @@ import { useEffect, useState } from 'react'; import { patchCounselorsAll } from 'api/patch'; export const BuyerHome = () => { const navigate = useNavigate(); - //결과저장 const [searchData, setSearchData] = useState([]); - const fectchSearchResults = async () => { - try { - const body = { - index: 0, - }; - const res: any = await patchCounselorsAll('POPULARITY', body); - if (res.status === 200) { - setSearchData(res.data); - } else if (res.response.status === 404) { - alert('유효하지 않은 정렬 방식입니다.'); - navigate('/share'); - } - } catch (e) { - console.log(e); - } - }; useEffect(() => { + const fectchSearchResults = async () => { + try { + const body = { + index: 0, + }; + const res: any = await patchCounselorsAll('POPULARITY', body); + if (res.status === 200) { + setSearchData(res.data); + } else if (res.response.status === 404) { + alert('유효하지 않은 정렬 방식입니다.'); + navigate('/share'); + } + } catch (e) { + console.log(e); + } + }; fectchSearchResults(); - }, []); + }, [navigate]); return (
{ const navigate = useNavigate(); - // Modal 여부(recoil), 스크롤 막기 + + // state with modal, scrollLock const [isModalOpen, setIsModalOpen] = useRecoilState(isSortModalOpenState); const setScrollLock = useSetRecoilState(scrollLockState); - // 검색 결과 리스트 - 상담사 리스트와 공개 상담 리스트 + + // state for saving search api result const [searchData, setSearchData] = useState([]); const [openConsultSearchData, setOpenConsultSearchData] = useState< openConsultApiObject[] >([]); - //무한스크롤 위한 page num과 lastId + + // state with inifinite scroll - page number and last id const [pageNum, setPageNum] = useState(0); const [lastId, setLastId] = useState(0); - // 무한스크롤을 trigger를 위한 상태 + + // state for trigger fetch api (infinite scroll) const [isLastElem, setIsLastElem] = useState(false); - // 무한스크롤을 trigger를 위한 상태 const [isLoading, setIsLoading] = useState(true); + + // state for blocking fetch api (infinite scroll) when the previous request has not been completed const preventRef = useRef(true); + const { handleClickTab, searchParams, @@ -63,6 +66,8 @@ export const BuyerSearchResult = () => { setSortType, handleChangeInput, } = useSearchPageParams(); + + // function to run in intersection observer const onIntersect: IntersectionObserverCallback = async (entry) => { if ( entry[0].isIntersecting && @@ -87,75 +92,85 @@ export const BuyerSearchResult = () => { onIntersect, }); - const fetchSearchResults = async (searchWord: string, pageIndex: number) => { - try { - const body = { - word: searchWord, - index: pageIndex, - }; - const sortTypeString: string = ConverSortType(sortType); - const res: any = await patchSearchWordsCounselorsResults( - sortTypeString, - body, - ); - if (res.status === 200) { - if (res.data.length !== 0) { - if (pageIndex === 0) { - setSearchData(res.data); - setPageNum(pageNum + 1); + // function for fetching search result + const fetchSearchResults = useCallback( + async (searchWord: string, pageIndex: number) => { + try { + const body = { + word: searchWord, + index: pageIndex, + }; + const sortTypeString: string = ConverSortType(sortType); + const res: any = await patchSearchWordsCounselorsResults( + sortTypeString, + body, + ); + if (res.status === 200) { + if (res.data.length !== 0) { + if (pageIndex === 0) { + setSearchData(res.data); + setPageNum(pageNum + 1); + } else { + const updatedSearchs = [...searchData, ...res.data]; + setSearchData(updatedSearchs); + setPageNum(pageNum + 1); + } } else { - const updatedSearchs = [...searchData, ...res.data]; - setSearchData(updatedSearchs); - setPageNum(pageNum + 1); + setIsLastElem(true); } - } else { - setIsLastElem(true); + } else if (res.response.status === 400) { + alert('검색어는 2~20자 사이여야 합니다.'); + } + } catch (e) { + alert(e); + } finally { + if (pageIndex === 0) { + setIsLoading(false); } - } else if (res.response.status === 400) { - alert('검색어는 2~20자 사이여야 합니다.'); - } - } catch (e) { - alert(e); - } finally { - if (pageIndex === 0) { - setIsLoading(false); } - } - }; - - const fetchOpenSearchResults = async (searchWord: string, postId: number) => { - try { - const body = { - word: searchWord, - postId: postId, - }; - const sortTypeString: string = ConverOpenSortType(openSortType); - const res: any = await patchSearchWordsPostsResults(sortTypeString, body); - if (res.status === 200) { - if (res.data.length !== 0) { - if (postId === 0) { - setOpenConsultSearchData(res.data); - setPageNum(pageNum + 1); + }, + [pageNum, searchData, sortType], + ); + const fetchOpenSearchResults = useCallback( + async (searchWord: string, postId: number) => { + try { + const body = { + word: searchWord, + postId: postId, + }; + const sortTypeString: string = ConvertOpenSortType(openSortType); + const res: any = await patchSearchWordsPostsResults( + sortTypeString, + body, + ); + if (res.status === 200) { + if (res.data.length !== 0) { + if (postId === 0) { + setOpenConsultSearchData(res.data); + setPageNum(pageNum + 1); + } else { + const updatedSearchs = [...openConsultSearchData, ...res.data]; + setLastId(res.data.postId); + setOpenConsultSearchData(updatedSearchs); + } + setIsLastElem(true); } else { - const updatedSearchs = [...openConsultSearchData, ...res.data]; - setLastId(res.data.postId); - setOpenConsultSearchData(updatedSearchs); + setOpenConsultSearchData([]); } - setIsLastElem(true); - } else { - setOpenConsultSearchData([]); + } else if (res.response.status === 400) { + alert('검색어는 2~20자 사이여야 합니다.'); + } + } catch (e) { + alert(e); + } finally { + if (postId === 0) { + setIsLoading(false); } - } else if (res.response.status === 400) { - alert('검색어는 2~20자 사이여야 합니다.'); - } - } catch (e) { - alert(e); - } finally { - if (postId === 0) { - setIsLoading(false); } - } - }; + }, + [openConsultSearchData, openSortType, pageNum], + ); + useLayoutEffect(() => { setIsLastElem(false); setPageNum(0); diff --git a/src/utils/convertOpenSortType.ts b/src/utils/convertOpenSortType.ts index 27429624..8ff6b394 100644 --- a/src/utils/convertOpenSortType.ts +++ b/src/utils/convertOpenSortType.ts @@ -1,4 +1,4 @@ -export const ConverOpenSortType = (typeNum: number) => { +export const ConvertOpenSortType = (typeNum: number) => { if (typeNum === 0) { return 'LATEST'; } else if (typeNum === 1) { From d3df242a8eb2bff27d6aef71cfccf7415af0a0b2 Mon Sep 17 00:00:00 2001 From: inyoung Date: Sun, 21 Apr 2024 00:57:50 +0900 Subject: [PATCH 10/18] Feat: Changed the previous search value storage method from recoil to query string navigate method.#233 --- src/components/Buyer/BuyerSearch/SearchHeader.tsx | 6 +----- src/hooks/useSearchPageParams.ts | 8 ++------ 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/src/components/Buyer/BuyerSearch/SearchHeader.tsx b/src/components/Buyer/BuyerSearch/SearchHeader.tsx index b18b73ca..4bd3aee9 100644 --- a/src/components/Buyer/BuyerSearch/SearchHeader.tsx +++ b/src/components/Buyer/BuyerSearch/SearchHeader.tsx @@ -5,12 +5,9 @@ import { useNavigate } from 'react-router-dom'; import { Grey1, Grey4, White } from 'styles/color'; import Input from 'components/Common/Input'; import { ChangeEvent, useState } from 'react'; -import { useSetRecoilState } from 'recoil'; -import { searchKeywordState } from 'utils/atom'; export const SearchHeader = () => { const navigate = useNavigate(); - const setKeyword = useSetRecoilState(searchKeywordState); //input value const [input, setInput] = useState(''); //input onchagne @@ -19,8 +16,7 @@ export const SearchHeader = () => { }; const handleSubmit = (event: ChangeEvent) => { event.preventDefault(); - setKeyword(input); - navigate('/search/result'); + navigate(`/search/result?keyword=${input}`); }; return ( diff --git a/src/hooks/useSearchPageParams.ts b/src/hooks/useSearchPageParams.ts index 9034a6aa..90e18d58 100644 --- a/src/hooks/useSearchPageParams.ts +++ b/src/hooks/useSearchPageParams.ts @@ -1,7 +1,5 @@ import { ChangeEvent, useCallback, useState } from 'react'; import { useSearchParams } from 'react-router-dom'; -import { useRecoilValue } from 'recoil'; -import { searchKeywordState } from 'utils/atom'; // // @@ -17,8 +15,7 @@ export const useSearchPageParams = () => { const openSortParam = searchParams.get('open-sort'); /** get initial values */ - const keywordFromPrevPage = useRecoilValue(searchKeywordState); - const initialKeyword = keywordParam ?? keywordFromPrevPage; + const initialKeyword = keywordParam ?? ''; const initialSearchType = typeParam ?? 'counselor'; const initialSortType = sortParam === 'rating' ? 2 : sortParam === 'popular' ? 1 : 0; @@ -27,7 +24,7 @@ export const useSearchPageParams = () => { const initialTabState = typeParam === 'open-consult' ? 2 : 1; /** states with iniitial value */ - const [input, setInput] = useState(keywordFromPrevPage); + const [input, setInput] = useState(initialKeyword); const [keyword, setKeyword] = useState(initialKeyword); const [searchType, setSearchType] = useState(initialSearchType); const [openSortType, setOpenSortType] = useState(initialOpenSortType); @@ -88,7 +85,6 @@ export const useSearchPageParams = () => { keyword, setKeyword, handleSubmit, - keywordFromPrevPage, searchType, sortType, setSortType, From d68b37cf8f42fd07fb54afd396f243d2199a3b08 Mon Sep 17 00:00:00 2001 From: inyoung Date: Sun, 21 Apr 2024 02:40:12 +0900 Subject: [PATCH 11/18] =?UTF-8?q?Hotfix:=20=EB=B0=B1=EC=97=94=EB=93=9C=20?= =?UTF-8?q?=EC=9D=91=EB=8B=B5=EC=9D=B4=20=EB=B3=B4=EC=9D=B4=EC=A7=80=20?= =?UTF-8?q?=EC=95=8A=EC=9D=80=20=EC=9D=B4=EC=8A=88=EB=A1=9C=20=EC=9D=B8?= =?UTF-8?q?=ED=95=98=EC=97=AC=20=EC=9E=84=EC=8B=9C=EC=A0=81=EC=9C=BC?= =?UTF-8?q?=EB=A1=9C=20response=20status=EC=97=90=20=EB=8C=80=ED=95=9C=20?= =?UTF-8?q?=EC=B2=98=EB=A6=AC=20=EC=97=86=EC=9D=B4=20=EA=B3=B5=EA=B0=9C?= =?UTF-8?q?=EC=83=81=EB=8B=B4=20=EC=9E=91=EC=84=B1=20#228?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../BuyerWriteOpenConsult/FianlWritePopup.tsx | 14 +++----------- .../Seller/SellerOpenConsult/IsSendPopup.tsx | 1 + 2 files changed, 4 insertions(+), 11 deletions(-) diff --git a/src/components/Buyer/BuyerWriteOpenConsult/FianlWritePopup.tsx b/src/components/Buyer/BuyerWriteOpenConsult/FianlWritePopup.tsx index bb2b4c61..3e596f04 100644 --- a/src/components/Buyer/BuyerWriteOpenConsult/FianlWritePopup.tsx +++ b/src/components/Buyer/BuyerWriteOpenConsult/FianlWritePopup.tsx @@ -5,11 +5,7 @@ import { useSetRecoilState } from 'recoil'; import styled from 'styled-components'; import { Green, Grey4, LightGreen, White } from 'styles/color'; import { Body1, Body3 } from 'styles/font'; -import { - isBuyPopupOpenState, - isPostPopupOpenState, - isSendPopupOpenState, -} from 'utils/atom'; +import { isPostPopupOpenState } from 'utils/atom'; import { convertCategoryEnum } from 'utils/convertCategoryEnum'; interface FianlWritePopupProps { title: string; @@ -31,12 +27,8 @@ function FinalWritePopup({ title, content, category }: FianlWritePopupProps) { isCompleted: true, }; try { - const res: any = await patchOpenConsult(body); - if (res?.status === 200) { - navigate('/consult/?type=open-consult'); - } else { - console.log(res); - } + await patchOpenConsult(body); + navigate('/consult/?type=open-consult'); } catch (err) { alert(err); } diff --git a/src/components/Seller/SellerOpenConsult/IsSendPopup.tsx b/src/components/Seller/SellerOpenConsult/IsSendPopup.tsx index 268f3503..52467063 100644 --- a/src/components/Seller/SellerOpenConsult/IsSendPopup.tsx +++ b/src/components/Seller/SellerOpenConsult/IsSendPopup.tsx @@ -23,6 +23,7 @@ function IsSendPopup({ text, setText, setIsReplying }: IsSendPopupProps) { }; try { const res: any = await postComment(body); + console.log(res); if (res?.status === 200 || res.status === 201) { setText(''); setIsSendPopupOpen(false); From 707b28ace648d54c368f18a329e8968f676d0a86 Mon Sep 17 00:00:00 2001 From: inyoung Date: Sun, 21 Apr 2024 18:04:23 +0900 Subject: [PATCH 12/18] =?UTF-8?q?Fix:=20api=20=EC=9D=B8=EC=8A=A4=ED=84=B4?= =?UTF-8?q?=EC=8A=A4=20=EB=B6=80=EB=B6=84=20=EC=88=98=EC=A0=95=ED=95=98?= =?UTF-8?q?=EC=97=AC=20=EA=B3=B5=EA=B0=9C=EC=83=81=EB=8B=B4=20=EC=9E=91?= =?UTF-8?q?=EC=84=B1=20=EC=8B=9C=20api=20response=20status=EC=97=90=20?= =?UTF-8?q?=EB=94=B0=EB=A5=B8=20=EC=98=88=EC=99=B8=EC=B2=98=EB=A6=AC=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84=20#236?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Router.tsx | 2 +- src/api/patch.ts | 3 +-- .../BuyerWriteOpenConsult/FianlWritePopup.tsx | 15 +++++++++++++-- src/pages/Buyer/BuyerOpenPaymentDetail.tsx | 13 +++++++++++-- 4 files changed, 26 insertions(+), 7 deletions(-) diff --git a/src/Router.tsx b/src/Router.tsx index 417189f6..792ebe0c 100644 --- a/src/Router.tsx +++ b/src/Router.tsx @@ -80,7 +80,7 @@ const Router = () => { } /> } /> } /> - } /> + } /> } /> } /> } /> diff --git a/src/api/patch.ts b/src/api/patch.ts index 01c3115f..b92c690d 100644 --- a/src/api/patch.ts +++ b/src/api/patch.ts @@ -94,6 +94,5 @@ export const patchAdoptComment = async (postId: any, commentId: string) => await patchInstance(`/comments/customers/${postId}?commentId=${commentId}`); //Post Controller -export const patchOpenConsult = async (body: any) => { +export const patchOpenConsult = async (body: any) => await patchInstance(`/posts`, body); -}; diff --git a/src/components/Buyer/BuyerWriteOpenConsult/FianlWritePopup.tsx b/src/components/Buyer/BuyerWriteOpenConsult/FianlWritePopup.tsx index 3e596f04..2bb2b377 100644 --- a/src/components/Buyer/BuyerWriteOpenConsult/FianlWritePopup.tsx +++ b/src/components/Buyer/BuyerWriteOpenConsult/FianlWritePopup.tsx @@ -27,8 +27,19 @@ function FinalWritePopup({ title, content, category }: FianlWritePopupProps) { isCompleted: true, }; try { - await patchOpenConsult(body); - navigate('/consult/?type=open-consult'); + const res: any = await patchOpenConsult(body); + if (res.status === 200) { + navigate('/consult/?type=open-consult'); + } else if (res?.response.status === 400) { + alert('이미 최종 제출된 상담입니다.'); + navigate('/consult?type=open-consult'); + } else if (res?.response.status === 403) { + alert('작성권한이 없습니다.'); + navigate('/consult?type=open-consult'); + } else if (res?.response.status === 404) { + alert('존재하지 않는 일대다상담입니다.'); + navigate('/consult?type=open-consult'); + } } catch (err) { alert(err); } diff --git a/src/pages/Buyer/BuyerOpenPaymentDetail.tsx b/src/pages/Buyer/BuyerOpenPaymentDetail.tsx index 26ed2efc..874cc769 100644 --- a/src/pages/Buyer/BuyerOpenPaymentDetail.tsx +++ b/src/pages/Buyer/BuyerOpenPaymentDetail.tsx @@ -16,8 +16,17 @@ export const BuyerOpenPaymentDetail = () => { cost: 500, isPublic: false, }; - await postOpenConsult(body); - navigate('/paymentComplete'); + try { + const res: any = await postOpenConsult(body); + if (res.status === 201) { + console.log(res); + navigate('/paymentComplete'); + } else if (res?.response.status === 404) { + alert('존재하지 않는 회원입니다.'); + } + } catch (err) { + alert(err); + } }; return ( From 061eec04ee66249ca4479df959cacac983ce91d3 Mon Sep 17 00:00:00 2001 From: inyoung Date: Sun, 21 Apr 2024 18:29:01 +0900 Subject: [PATCH 13/18] =?UTF-8?q?Fix:=20sort=20modal=20props=20error=20?= =?UTF-8?q?=ED=95=B4=EA=B2=B0,=20=EB=93=A4=EC=A4=80=EB=A7=88=EC=99=80=20?= =?UTF-8?q?=EC=B9=B4=ED=85=8C=EA=B3=A0=EB=A6=AC=20=ED=8E=98=EC=9D=B4?= =?UTF-8?q?=EC=A7=80=EC=97=90=EC=84=9C=20=EC=BF=BC=EB=A6=AC=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=EB=A7=81=EC=9C=BC=EB=A1=9C=20=ED=95=84=ED=84=B0?= =?UTF-8?q?=EB=A7=81=20=EA=B5=AC=ED=98=84=20#233?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/post.ts | 2 +- .../BuyerWriteOpenConsult/FianlWritePopup.tsx | 5 +-- src/pages/Buyer/BuyerAvailCounselor.tsx | 16 ++++++- src/pages/Buyer/BuyerCategoryResult.tsx | 45 +++++++++++++------ src/pages/Buyer/BuyerOpenPaymentDetail.tsx | 4 +- src/pages/Buyer/BuyerWriteOpenConsult.tsx | 2 +- src/pages/Seller/SellerLetterWrite.tsx | 5 +-- 7 files changed, 53 insertions(+), 26 deletions(-) diff --git a/src/api/post.ts b/src/api/post.ts index 74e80e24..ef518389 100644 --- a/src/api/post.ts +++ b/src/api/post.ts @@ -50,7 +50,7 @@ export const postWishLists = async (body: any) => // 일대다상담 // Comment Controller -export const postComment = async (body) => +export const postComment = async (body: any) => await postInstance('/comments/counselors', body); // CommentLike Controller diff --git a/src/components/Buyer/BuyerWriteOpenConsult/FianlWritePopup.tsx b/src/components/Buyer/BuyerWriteOpenConsult/FianlWritePopup.tsx index 2bb2b377..ff109a35 100644 --- a/src/components/Buyer/BuyerWriteOpenConsult/FianlWritePopup.tsx +++ b/src/components/Buyer/BuyerWriteOpenConsult/FianlWritePopup.tsx @@ -1,10 +1,9 @@ import { patchOpenConsult } from 'api/patch'; -import React from 'react'; import { useNavigate, useParams } from 'react-router-dom'; import { useSetRecoilState } from 'recoil'; import styled from 'styled-components'; -import { Green, Grey4, LightGreen, White } from 'styles/color'; -import { Body1, Body3 } from 'styles/font'; +import { Green, LightGreen, White } from 'styles/color'; +import { Body1 } from 'styles/font'; import { isPostPopupOpenState } from 'utils/atom'; import { convertCategoryEnum } from 'utils/convertCategoryEnum'; interface FianlWritePopupProps { diff --git a/src/pages/Buyer/BuyerAvailCounselor.tsx b/src/pages/Buyer/BuyerAvailCounselor.tsx index 7b91101c..c183ff3f 100644 --- a/src/pages/Buyer/BuyerAvailCounselor.tsx +++ b/src/pages/Buyer/BuyerAvailCounselor.tsx @@ -1,4 +1,4 @@ -import { useNavigate } from 'react-router-dom'; +import { useNavigate, useSearchParams } from 'react-router-dom'; import styled from 'styled-components'; import { Grey1, Grey3, Grey6, White } from 'styles/color'; import { Button2, Heading } from 'styles/font'; @@ -18,7 +18,17 @@ import useIntersectionObserver from 'hooks/useIntersectionObserver'; export const BuyerAvailCounselor = () => { //0 : 최신순 1:인기순 2: 별점순 // 바뀔 때마다 useEffect로 request - const [sortType, setSortType] = useState(0); + // using query params to store sorting type + const [searchParams, setSearchParams] = useSearchParams(); + + // sortType - 0 : 최신순 1:인기순 2: 별점순, 기본값: 0, 최신순 + const initialSortType = + searchParams.get('sort') === 'rating' + ? 2 + : searchParams.get('sort') === 'popular' + ? 1 + : 0; + const [sortType, setSortType] = useState(initialSortType); // Modal 여부(recoil) const [isModalOpen, setIsModalOpen] = useRecoilState(isSortModalOpenState); @@ -125,6 +135,8 @@ export const BuyerAvailCounselor = () => { }} /> { const navigate = useNavigate(); - //0 : 최신순 1:인기순 2: 별점순 - // 바뀔 때마다 useEffect로 request - const [sortType, setSortType] = useState(0); - // Modal 여부(recoil) + + // using query params to store sorting type + const [searchParams, setSearchParams] = useSearchParams(); + + // sortType - 0 : 최신순 1:인기순 2: 별점순, 기본값: 0, 최신순 + const initialSortType = + searchParams.get('sort') === 'rating' + ? 2 + : searchParams.get('sort') === 'popular' + ? 1 + : 0; + const [sortType, setSortType] = useState(initialSortType); + + // state with opening Modal, lock scroll const [isModalOpen, setIsModalOpen] = useRecoilState(isSortModalOpenState); - //scorll 막기 const setScrollLock = useSetRecoilState(scrollLockState); - //const 카테고리 정보, 검색과 동일하게 searchKeyword로 넘겨줌 + + // state with store keyword and serach result const searchKeyword = useRecoilValue(searchKeywordState); - //결과저장 const [searchData, setSearchData] = useState([]); - //무한스크롤 위한 page num + + //page num for infinite scrol const [pageNum, setPageNum] = useState(0); const [isLastElem, setIsLastElem] = useState(false); - const [isLoading, setIsLoading] = useState(true); //로딩 state - const preventRef = useRef(true); // 중복 방지 옵션 - //fetch 함수 + + // loding spinner + const [isLoading, setIsLoading] = useState(true); + const preventRef = useRef(true); + + // functio with fetch API const fetchSearchResults = async (pageIndex: number) => { try { const body = { @@ -137,6 +154,8 @@ export const BuyerCategoryResult = () => { }} /> { if (input === '' || titleInput === '') { // 비어 있으면 전송못함 diff --git a/src/pages/Seller/SellerLetterWrite.tsx b/src/pages/Seller/SellerLetterWrite.tsx index 53c3ab64..74b2f256 100644 --- a/src/pages/Seller/SellerLetterWrite.tsx +++ b/src/pages/Seller/SellerLetterWrite.tsx @@ -1,16 +1,13 @@ -import { getDraftsLetter, getLetterRecentType } from 'api/get'; import { LetterWriteHeader } from 'components/Seller/SellerLetterWrite/LetterWriteHeader'; import { LetterWriteMainSection } from 'components/Seller/SellerLetterWrite/LetterWriteMainSection'; import { LetterWriteSuccess } from 'components/Seller/SellerLetterWrite/LetterWriteSuccess'; -import { useEffect, useState } from 'react'; -import { useNavigate, useParams } from 'react-router-dom'; +import { useState } from 'react'; export const SellerLetterWrite = () => { // 편지 전송 완료 페이지 띄울지 여부 const [isSend, setIsSend] = useState(false); // 셰어로부터 질문 한눈에 보기 여부 const [isViewQuestion, setIsViewQuestion] = useState(false); - const navigate = useNavigate(); return ( <> Date: Sun, 21 Apr 2024 19:24:47 +0900 Subject: [PATCH 14/18] Chore: delete useless import and styled-components error message #228 --- .../BuyerConsultLetterSection.tsx | 1 + .../BuyerOpenConsult/HotOpenConsultList.tsx | 3 +- .../BuyerOpenConsult/OpenConsultList.tsx | 146 ++++++++---------- src/components/Buyer/Common/ConsultCard.tsx | 10 +- src/components/Common/TabA1.tsx | 26 ++-- .../SellerConsult/SellerConsultSection.tsx | 29 ++-- src/pages/Buyer/BuyerConsult.tsx | 20 +-- 7 files changed, 113 insertions(+), 122 deletions(-) diff --git a/src/components/Buyer/BuyerConsult/BuyerConsultLetterSection.tsx b/src/components/Buyer/BuyerConsult/BuyerConsultLetterSection.tsx index 3f1e7b74..e46a413b 100644 --- a/src/components/Buyer/BuyerConsult/BuyerConsultLetterSection.tsx +++ b/src/components/Buyer/BuyerConsult/BuyerConsultLetterSection.tsx @@ -71,6 +71,7 @@ export const BuyerConsultLetterSection = ({ {cardData.map((value) => { return ( {hotConsultList.map((item) => ( { navigate(`/open-consult/${item.postId}`); }} diff --git a/src/components/Buyer/BuyerOpenConsult/OpenConsultList.tsx b/src/components/Buyer/BuyerOpenConsult/OpenConsultList.tsx index 67a52516..a1d51a1d 100644 --- a/src/components/Buyer/BuyerOpenConsult/OpenConsultList.tsx +++ b/src/components/Buyer/BuyerOpenConsult/OpenConsultList.tsx @@ -1,32 +1,23 @@ -import React, { useEffect, useLayoutEffect, useRef, useState } from 'react'; +import React, { useLayoutEffect, useRef, useState } from 'react'; import styled from 'styled-components'; -import { Green, Grey1, Grey2, Grey3, Grey6 } from 'styles/color'; -import { Body1, Caption1, Caption2 } from 'styles/font'; -import { LoadingSpinner } from 'utils/LoadingSpinner'; -import { ReactComponent as LockIcon } from 'assets/icons/icon-lock.svg'; +import { Grey1, Grey2, Grey6 } from 'styles/color'; +import { Body1, Caption1 } from 'styles/font'; import { ReactComponent as HeartIcon } from 'assets/icons/icon-heart2.svg'; -import { ReactComponent as HeartEmptyIcon } from 'assets/icons/icon-heart3.svg'; import { ReactComponent as SaveIcon } from 'assets/icons/icon-save2.svg'; -import { ReactComponent as SaveEmptyIcon } from 'assets/icons/icon-save3.svg'; import { ReactComponent as CommentIcon } from 'assets/icons/icon-comment.svg'; -import { ReactComponent as CheckIcon } from 'assets/icons/icon-check2.svg'; -import { ReactComponent as WriteIcon } from 'assets/icons/icon-write.svg'; import { Space } from 'components/Common/Space'; -import { BackDrop } from 'components/Common/BackDrop'; import { openConsultApiObject } from 'pages/Buyer/BuyerConsult'; -import { - getCustomerOpenConsultList, - getCustomerPublicConsultList, -} from 'api/get'; +import { getCustomerPublicConsultList } from 'api/get'; import { useNavigate } from 'react-router-dom'; import useIntersectionObserver from 'hooks/useIntersectionObserver'; +import { LoadingSpinner } from 'utils/LoadingSpinner'; function OpenConsultList() { const [cardData, setCardData] = useState([]); const preventRef = useRef(true); const navigate = useNavigate(); const [isLastElem, setIsLastElem] = useState(false); - const [isLoading, setIsLoading] = useState(false); + const [isLoading, setIsLoading] = useState(true); const onIntersect: IntersectionObserverCallback = async (entry) => { if (entry[0].isIntersecting && !isLastElem && preventRef.current) { preventRef.current = false; @@ -77,47 +68,63 @@ function OpenConsultList() { useLayoutEffect(() => { fetchOpenConsult(0, new Date().toISOString().slice(0, 19)); }, []); - return ( -
- - {/* 상담카드 부분 */} - {cardData.map((item) => ( - { - navigate(`/open-consult/${item.postId}`); - }} - > -
- {item.title} -
- -
{item.content}
-
- - - {item.totalLike} - - - - {item.totalScrap} - - - - {item.totalComment} - -
- {item.updatedAt} -
- ))} - {!isLastElem ? ( -
- ) : ( -
- )} - {/* 상담카드 부분 */} - -
- ); + if (isLoading) { + return ( +
+ +
+ ); + } else { + return ( +
+ + {/* 상담카드 부분 */} + {cardData.map((item) => ( + { + navigate(`/open-consult/${item.postId}`); + }} + key={item.postId} + > +
+ {item.title} +
+ +
{item.content}
+
+ + + {item.totalLike} + + + + {item.totalScrap} + + + + {item.totalComment} + +
+ {item.updatedAt} +
+ ))} + {!isLastElem ? ( +
+ ) : ( +
+ )} + {/* 상담카드 부분 */} + +
+ ); + } } const BuyerOpenConsultCardList = styled.div` display: flex; @@ -177,13 +184,6 @@ const HeartResizeIcon = styled(HeartIcon)` height: 2rem; `; -const PrivateSign = styled.div` - display: flex; - position: absolute; - top: 1.95rem; - right: 1.6rem; -`; - const TimeLeft = styled.div` font-size: 1.2rem; font-weight: 400; @@ -193,24 +193,4 @@ const TimeLeft = styled.div` right: 1.6rem; `; -const CreateConsultButton = styled.button` - width: 5.8rem; - height: 5.8rem; - border-radius: 100%; - background-color: ${Green}; - box-shadow: 0px 0px 12px 0px rgba(0, 0, 0, 0.25); - align-self: flex-end; -`; -const CreateConsultButtonWrapper = styled.div` - width: 100%; - padding: 0 3.3rem; - box-sizing: border-box; - display: flex; - position: fixed; - bottom: 3.5rem; - flex-direction: column; - @media (min-width: 768px) { - width: 375px; - } -`; export default OpenConsultList; diff --git a/src/components/Buyer/Common/ConsultCard.tsx b/src/components/Buyer/Common/ConsultCard.tsx index 6bd45fc7..9ff979fa 100644 --- a/src/components/Buyer/Common/ConsultCard.tsx +++ b/src/components/Buyer/Common/ConsultCard.tsx @@ -105,11 +105,11 @@ export const ConsultCard = ({ ) : null}
{latestMessageContent !== null ? ( - + {latestMessageContent} ) : ( - + {opponentNickname}님께 고민 내용을 남겨 주세요.{' '} {opponentNickname}님이 24시간 이내 답장을 드릴 거예요. @@ -151,7 +151,7 @@ const Unread = styled.div` justify-content: center; align-items: center; border-radius: 0.4rem; - background-color: ${Green}; + background-$color: ${Green}; width: 1.9rem; height: 1.9rem; right: 0; @@ -184,14 +184,14 @@ const ConsultStateBox = styled.div` align-items: center; } `; -export const CardText = styled.div<{ color: string }>` +export const CardText = styled.div<{ $color: string }>` width: 100%; display: -webkit-box; -webkit-box-orient: vertical; overflow: hidden; -webkit-line-clamp: 2; font-family: Pretendard; - color: ${(props) => props.color}; + color: ${(props) => props.$color}; font-size: 1.4rem; font-weight: 400; line-height: 155%; diff --git a/src/components/Common/TabA1.tsx b/src/components/Common/TabA1.tsx index 64a1f2db..b8b4ef1c 100644 --- a/src/components/Common/TabA1.tsx +++ b/src/components/Common/TabA1.tsx @@ -13,7 +13,7 @@ interface TabA1Props { } export const TabA1 = ({ isBuyer, initState }: TabA1Props) => { const navigate = useNavigate(); - const [tabState, setTabState] = useState(); + const [$tabState, setTabState] = useState(); const [color, setColor] = useState(); const navigateOpenConsult = async () => { try { @@ -37,7 +37,7 @@ export const TabA1 = ({ isBuyer, initState }: TabA1Props) => { return ( { setTabState(1); if (isBuyer) { @@ -47,7 +47,7 @@ export const TabA1 = ({ isBuyer, initState }: TabA1Props) => { } }} > - {tabState === 1 ? ( + {$tabState === 1 ? ( <> @@ -57,7 +57,7 @@ export const TabA1 = ({ isBuyer, initState }: TabA1Props) => { )} { setTabState(2); if (isBuyer) { @@ -67,7 +67,7 @@ export const TabA1 = ({ isBuyer, initState }: TabA1Props) => { } }} > - {tabState === 2 ? ( + {$tabState === 2 ? ( <> 상담 @@ -77,7 +77,7 @@ export const TabA1 = ({ isBuyer, initState }: TabA1Props) => { )} { setTabState(3); if (isBuyer) { @@ -87,7 +87,7 @@ export const TabA1 = ({ isBuyer, initState }: TabA1Props) => { } }} > - {tabState === 3 ? ( + {$tabState === 3 ? ( <> 공개상담 @@ -96,8 +96,8 @@ export const TabA1 = ({ isBuyer, initState }: TabA1Props) => { 공개상담 )} - - {tabState === 4 ? ( + + {$tabState === 4 ? ( <> 내 정보 @@ -132,15 +132,15 @@ const Wrapper = styled.nav` background-color: white; z-index: 999; `; -const TabButton = styled.div<{ tabState: number }>` +const TabButton = styled.div<{ $tabState: number }>` display: flex; flex-direction: column; width: ${(props) => - props?.tabState === 1 + props?.$tabState === 1 ? '8.8rem' - : props?.tabState === 2 + : props?.$tabState === 2 ? '8.2rem' - : props?.tabState === 3 + : props?.$tabState === 3 ? '10.9rem' : '9.6rem'}; align-items: center; diff --git a/src/components/Seller/SellerConsult/SellerConsultSection.tsx b/src/components/Seller/SellerConsult/SellerConsultSection.tsx index 5c9e96cd..d64dc89c 100644 --- a/src/components/Seller/SellerConsult/SellerConsultSection.tsx +++ b/src/components/Seller/SellerConsult/SellerConsultSection.tsx @@ -10,12 +10,20 @@ import SellerLetterList from './SellerLetterList'; import SellerChatList from './SellerChatList'; import SellerOpenConsultList from './SellerOpenConsultList'; import { useConsultParams } from 'hooks/useConsultParams'; -import { useState } from 'react'; + +// +// +// + interface ConsultTypeProps { - isActive: boolean; - isLong?: boolean; + $isActive: boolean; + $isLong?: boolean; } +// +// +// + export const SellerConsultSection = () => { const { consultType, @@ -30,27 +38,26 @@ export const SellerConsultSection = () => { setIsChecked, } = useConsultParams(); - // 모달 열지 말지 const setIsModalOpen = useSetRecoilState(isConsultModalOpenState); return ( <>
편지 채팅 공개상담 @@ -135,7 +142,7 @@ const ConsultSortingMenu = styled.div` const ConsultType = styled.div` display: flex; - width: ${(props) => (props.isLong ? '8rem' : '5.6rem')}; + width: ${(props) => (props.$isLong ? '8rem' : '5.6rem')}; height: 3.4rem; cursor: pointer; justify-content: center; @@ -145,8 +152,8 @@ const ConsultType = styled.div` font-style: normal; font-weight: 400; line-height: 110%; - background: ${(props) => (props.isActive ? LightGreen : Grey6)}; - color: ${(props) => (props.isActive ? Green : Black)}; + background: ${(props) => (props.$isActive ? LightGreen : Grey6)}; + color: ${(props) => (props.$isActive ? Green : Black)}; margin-top: 0.2rem; `; const SortingType = styled.div` diff --git a/src/pages/Buyer/BuyerConsult.tsx b/src/pages/Buyer/BuyerConsult.tsx index 643654e6..1d7d586a 100644 --- a/src/pages/Buyer/BuyerConsult.tsx +++ b/src/pages/Buyer/BuyerConsult.tsx @@ -50,9 +50,7 @@ export interface openConsultApiObject { export const BuyerConsult = () => { const navigate = useNavigate(); - const sortList = ['최근순', '읽지않은순']; - const { consultType, sortType, @@ -85,7 +83,7 @@ export const BuyerConsult = () => {
@@ -93,7 +91,7 @@ export const BuyerConsult = () => { @@ -101,7 +99,7 @@ export const BuyerConsult = () => { @@ -163,10 +161,14 @@ export const BuyerConsult = () => { }; const Wrapper = styled.div` .options { - padding: 0.8rem 2rem 0rem; + padding: 0.8rem 2rem 1.6rem; display: flex; flex-direction: column; gap: 1.2rem; + position: sticky; + top: 10.5rem; + background-color: white; + z-index: 10; } .select { display: flex; @@ -177,7 +179,7 @@ const Wrapper = styled.div` .select-wrapper { display: flex; gap: 0.4rem; - cursor: pointer; + cursor: pointer; } .select-button { display: flex; @@ -188,8 +190,8 @@ const Wrapper = styled.div` gap: 0.4rem; } `; -const SelectButton = styled.div<{ isSelected: boolean; isLong?: boolean }>` - background-color: ${(props) => (props.isSelected ? LightGreen : Grey6)}; +const SelectButton = styled.div<{ $isSelected: boolean; $isLong?: boolean }>` + background-color: ${(props) => (props.$isSelected ? LightGreen : Grey6)}; cursor: pointer; padding: 0.8rem 1.6rem; border-radius: 1.2rem; From 5b045b2f9de32a396fc4a7f5ac760d4e564f8e71 Mon Sep 17 00:00:00 2001 From: inyoung Date: Sun, 21 Apr 2024 19:45:57 +0900 Subject: [PATCH 15/18] Feat: Fix the options tab in the buyer and counselor side consultation tab with a sticky container #224 --- .../BuyerConsult/BuyerConsultChatSection.tsx | 3 +- .../BuyerConsultLetterSection.tsx | 3 +- .../BuyerConsult/BuyerOpenConsultSection.tsx | 8 ++- .../SellerConsult/SellerConsultSection.tsx | 63 +++++++++++-------- src/pages/Seller/SellerConsult.tsx | 8 +-- 5 files changed, 48 insertions(+), 37 deletions(-) diff --git a/src/components/Buyer/BuyerConsult/BuyerConsultChatSection.tsx b/src/components/Buyer/BuyerConsult/BuyerConsultChatSection.tsx index b90412ce..35214f48 100644 --- a/src/components/Buyer/BuyerConsult/BuyerConsultChatSection.tsx +++ b/src/components/Buyer/BuyerConsult/BuyerConsultChatSection.tsx @@ -9,6 +9,7 @@ import { LoadingSpinner } from 'utils/LoadingSpinner'; import { Heading } from 'styles/font'; import { ReactComponent as Empty } from 'assets/icons/graphic-noting.svg'; import { convertChatListDate } from 'utils/convertDate'; +import { Space } from 'components/Common/Space'; // // @@ -261,6 +262,7 @@ export const BuyerConsultChatSection = ({ /> ); })} + ); } else { @@ -279,7 +281,6 @@ const BuyerConsultChatSectionWrapper = styled.section` flex-direction: column; gap: 0.8rem; align-items: center; - padding: 1.2rem 0; `; const EmptyWrapper = styled.div` margin-top: 10vh; diff --git a/src/components/Buyer/BuyerConsult/BuyerConsultLetterSection.tsx b/src/components/Buyer/BuyerConsult/BuyerConsultLetterSection.tsx index e46a413b..f0c4f329 100644 --- a/src/components/Buyer/BuyerConsult/BuyerConsultLetterSection.tsx +++ b/src/components/Buyer/BuyerConsult/BuyerConsultLetterSection.tsx @@ -7,6 +7,7 @@ import { getLettersCustomers } from 'api/get'; import { LoadingSpinner } from 'utils/LoadingSpinner'; import { ReactComponent as Empty } from 'assets/icons/graphic-noting.svg'; import { Heading } from 'styles/font'; +import { Space } from 'components/Common/Space'; interface BuyerConsultLetterSectionProps { sortType: number; @@ -86,6 +87,7 @@ export const BuyerConsultLetterSection = ({ /> ); })} + ) : ( @@ -103,7 +105,6 @@ const BuyerConsultLetterSectionWrapper = styled.section` flex-direction: column; gap: 0.8rem; align-items: center; - padding: 1.2rem 0; `; const EmptyIcon = styled(Empty)` diff --git a/src/components/Buyer/BuyerConsult/BuyerOpenConsultSection.tsx b/src/components/Buyer/BuyerConsult/BuyerOpenConsultSection.tsx index bc848908..2fdf56f6 100644 --- a/src/components/Buyer/BuyerConsult/BuyerOpenConsultSection.tsx +++ b/src/components/Buyer/BuyerConsult/BuyerOpenConsultSection.tsx @@ -1,7 +1,7 @@ import React, { useLayoutEffect, useRef, useState } from 'react'; import { useRecoilState } from 'recoil'; import styled from 'styled-components'; -import { Green, Grey1, Grey2, Grey3, Grey6 } from 'styles/color'; +import { Grey1, Grey2, Grey3, Grey6 } from 'styles/color'; import { Body1, Body3, Caption1, Heading } from 'styles/font'; import { LoadingSpinner } from 'utils/LoadingSpinner'; import { isBuyPopupOpenState } from 'utils/atom'; @@ -83,7 +83,7 @@ function BuyerOpenConsultSection({ isChecked }: BuyerOpenConsultSectionProps) { {isLoading ? (
)} {!isLastElem ? ( @@ -196,7 +198,7 @@ function BuyerOpenConsultSection({ isChecked }: BuyerOpenConsultSectionProps) { const BuyerOpenConsultCardList = styled.div` display: flex; - margin: 1.2rem 2rem; + margin: 0 2rem; flex-direction: column; align-items: flex-start; gap: 1.2rem; diff --git a/src/components/Seller/SellerConsult/SellerConsultSection.tsx b/src/components/Seller/SellerConsult/SellerConsultSection.tsx index d64dc89c..fc41de02 100644 --- a/src/components/Seller/SellerConsult/SellerConsultSection.tsx +++ b/src/components/Seller/SellerConsult/SellerConsultSection.tsx @@ -10,6 +10,7 @@ import SellerLetterList from './SellerLetterList'; import SellerChatList from './SellerChatList'; import SellerOpenConsultList from './SellerOpenConsultList'; import { useConsultParams } from 'hooks/useConsultParams'; +import { Space } from 'components/Common/Space'; // // @@ -92,38 +93,48 @@ export const SellerConsultSection = () => {
- {consultType === 'letter' ? ( - - ) : consultType === 'chat' ? ( - - ) : ( - - )} +
+ {consultType === 'letter' ? ( + + ) : consultType === 'chat' ? ( + + ) : ( + + )} + +
); }; -const ConsultSortingMenu = styled.div` +const ConsultSortingMenu = styled.section` display: flex; flex-direction: column; - margin: 0.8rem 2rem 1.2rem; + padding: 0.8rem 2rem 1.2rem; gap: 1.2rem; + position: sticky; + top: 10.4rem; + z-index: 10; + background-color: white; .row1 { display: flex; gap: 1.2rem; diff --git a/src/pages/Seller/SellerConsult.tsx b/src/pages/Seller/SellerConsult.tsx index a264a6bd..9a127cbb 100644 --- a/src/pages/Seller/SellerConsult.tsx +++ b/src/pages/Seller/SellerConsult.tsx @@ -14,12 +14,8 @@ export const SellerConsult = () => { }} /> -
- -
+ + ); }; From b3802b0fecd8a7a37b3d39c3f93a50f5d4e55a52 Mon Sep 17 00:00:00 2001 From: inyoung Date: Sun, 21 Apr 2024 22:03:04 +0900 Subject: [PATCH 16/18] =?UTF-8?q?Feat:=20=EB=A7=88=EC=9D=B8=EB=8D=94=20?= =?UTF-8?q?=EC=88=98=EC=9D=B5=20=EA=B4=80=EB=A6=AC=20=ED=8E=98=EC=9D=B4?= =?UTF-8?q?=EC=A7=80=20=ED=8E=98=EC=9D=B4=EC=A7=80=20=EC=8A=A4=ED=83=80?= =?UTF-8?q?=EC=9D=BC=20=EC=88=98=EC=A0=95=20=EB=B0=8F=20=EB=AC=B4=ED=95=9C?= =?UTF-8?q?=20=EC=8A=A4=ED=81=AC=EB=A1=A4=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Common/TabA1.tsx | 12 +- .../CompleteApplyPopup.tsx | 18 +- .../ManagementHeader.tsx | 7 +- .../ManagementStatusSelector.tsx | 24 ++- .../SellerCalculateCard.tsx | 22 ++- .../SellerManagementModal.tsx | 13 +- .../SellerConsult/SellerOpenConsultList.tsx | 2 - .../SellerHome/ConsultReviewsSection.tsx | 5 +- .../Seller/SellerCalculateManagement.tsx | 157 ++++++++++++------ 9 files changed, 181 insertions(+), 79 deletions(-) diff --git a/src/components/Common/TabA1.tsx b/src/components/Common/TabA1.tsx index b8b4ef1c..a4e78f6e 100644 --- a/src/components/Common/TabA1.tsx +++ b/src/components/Common/TabA1.tsx @@ -13,7 +13,7 @@ interface TabA1Props { } export const TabA1 = ({ isBuyer, initState }: TabA1Props) => { const navigate = useNavigate(); - const [$tabState, setTabState] = useState(); + const [tabState, setTabState] = useState(); const [color, setColor] = useState(); const navigateOpenConsult = async () => { try { @@ -33,7 +33,7 @@ export const TabA1 = ({ isBuyer, initState }: TabA1Props) => { } else { setColor(Green); } - }, []); + }, [initState, isBuyer]); return ( { } }} > - {$tabState === 1 ? ( + {tabState === 1 ? ( <> @@ -67,7 +67,7 @@ export const TabA1 = ({ isBuyer, initState }: TabA1Props) => { } }} > - {$tabState === 2 ? ( + {tabState === 2 ? ( <> 상담 @@ -87,7 +87,7 @@ export const TabA1 = ({ isBuyer, initState }: TabA1Props) => { } }} > - {$tabState === 3 ? ( + {tabState === 3 ? ( <> 공개상담 @@ -97,7 +97,7 @@ export const TabA1 = ({ isBuyer, initState }: TabA1Props) => { )} - {$tabState === 4 ? ( + {tabState === 4 ? ( <> 내 정보 diff --git a/src/components/Seller/SellerCalculateManagement/CompleteApplyPopup.tsx b/src/components/Seller/SellerCalculateManagement/CompleteApplyPopup.tsx index 63ac89d1..9a8003ee 100644 --- a/src/components/Seller/SellerCalculateManagement/CompleteApplyPopup.tsx +++ b/src/components/Seller/SellerCalculateManagement/CompleteApplyPopup.tsx @@ -4,20 +4,34 @@ import React from 'react'; import styled from 'styled-components'; import { Grey4 } from 'styles/color'; import { Body1, Body3 } from 'styles/font'; + +// +// +// + interface CompleteApplyPopupProps { name: string; date: string; + consultType: string; setIsCompleteApplyManage: React.Dispatch>; } + +// +// +// + function CompleteApplyPopup({ name = '김고민', - date = '2023.02.02', + date = '2001.12.15', + consultType = '무슨', setIsCompleteApplyManage, }: CompleteApplyPopupProps) { return ( 정산 신청이 완료되었어요! - {name}과의 편지 상담 2023.12.23 + + {name}과의 {consultType} 상담 {date} +