From b7b5e2dac2abbb8801f3e5910898a7c5a23f1036 Mon Sep 17 00:00:00 2001 From: dbstj0403 Date: Tue, 20 May 2025 08:58:07 +0900 Subject: [PATCH 1/9] =?UTF-8?q?Fix:=20=EA=B3=B5=EA=B3=A0=20=EC=A3=BC?= =?UTF-8?q?=EA=B4=80=EA=B8=B0=EA=B4=80=20=ED=91=9C=EA=B8=B0=20=EC=95=88?= =?UTF-8?q?=EB=90=98=EB=8D=98=20=EC=98=A4=EB=A5=98=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/myPageDetail/MyPageDetailPage.jsx | 4 ++-- src/pages/support/SupportItemPage.jsx | 19 +++---------------- src/pages/support/components/FOAItem.jsx | 8 +++++--- src/pages/support/utils/dateFunc.js | 4 ++++ 4 files changed, 14 insertions(+), 21 deletions(-) diff --git a/src/pages/myPageDetail/MyPageDetailPage.jsx b/src/pages/myPageDetail/MyPageDetailPage.jsx index a49a1f3..78a2a54 100644 --- a/src/pages/myPageDetail/MyPageDetailPage.jsx +++ b/src/pages/myPageDetail/MyPageDetailPage.jsx @@ -38,7 +38,7 @@ const MyPageDetailPage = () => { {kind === "찜" && (
-
+

저장한 장소

@@ -48,7 +48,7 @@ const MyPageDetailPage = () => { {kind === "응원" && (
-
+

내가 응원한 이야기

diff --git a/src/pages/support/SupportItemPage.jsx b/src/pages/support/SupportItemPage.jsx index 7b193d6..139825f 100644 --- a/src/pages/support/SupportItemPage.jsx +++ b/src/pages/support/SupportItemPage.jsx @@ -1,21 +1,8 @@ import { useParams, useNavigate } from "react-router-dom"; import { useEffect } from "react"; import { useGetFOADetail } from "@/apis/announcement/queries"; - -// 날짜 형식 확인 함수 (YYYY-MM-DD) -const isValidDateFormat = (dateStr) => { - return /^\d{4}-\d{2}-\d{2}$/.test(dateStr); -}; - -// D-day 계산 함수 -const calculateDday = (dateStr) => { - const today = new Date(); - today.setHours(0, 0, 0, 0); - const endDate = new Date(dateStr); - endDate.setHours(0, 0, 0, 0); - const diffTime = endDate.getTime() - today.getTime(); - return Math.ceil(diffTime / (1000 * 60 * 60 * 24)); -}; +import { isValidDateFormat } from "@/pages/support/utils/dateFunc"; +import { calculateDday } from "@/pages/support/utils/dateFunc"; const SupportItemPage = () => { const navigate = useNavigate(); @@ -69,7 +56,7 @@ const SupportItemPage = () => {

{data?.title}

-

{data?.agency}

+

{data?.organization}

신청기간

diff --git a/src/pages/support/components/FOAItem.jsx b/src/pages/support/components/FOAItem.jsx index f03c709..d8ed7f3 100644 --- a/src/pages/support/components/FOAItem.jsx +++ b/src/pages/support/components/FOAItem.jsx @@ -6,9 +6,11 @@ const FOAItem = ({ data }) => { const formattedEndDate = formatEndDate(data.endDate); const dday = calculateDday(data.endDate); + console.log(data); + return (
{ if (data?.id) { navigate(`/support/list/${data.id}`); @@ -38,8 +40,8 @@ const FOAItem = ({ data }) => { {/* 본문: 텍스트 내용 */}
-

{data.title}

-

{data.agency}

+

{data?.title}

+

{data?.organization}

); diff --git a/src/pages/support/utils/dateFunc.js b/src/pages/support/utils/dateFunc.js index 25d2906..afd9dac 100644 --- a/src/pages/support/utils/dateFunc.js +++ b/src/pages/support/utils/dateFunc.js @@ -18,3 +18,7 @@ export const calculateDday = (raw) => { const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24)); return diffDays >= 0 ? `D-${diffDays}` : null; }; + +export const isValidDateFormat = (dateStr) => { + return /^\d{4}-\d{2}-\d{2}$/.test(dateStr); +}; From 9154bacce607af3c6ad958be9197a5f3d9d912a7 Mon Sep 17 00:00:00 2001 From: dbstj0403 Date: Tue, 20 May 2025 09:52:45 +0900 Subject: [PATCH 2/9] =?UTF-8?q?Refactor:=20useLikeToggle=20hooks=20?= =?UTF-8?q?=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/hooks/useLikeToggle.js | 72 +++++++++++++++++ src/pages/review/components/PlaceInfo.jsx | 77 +++++-------------- src/pages/review/components/Reviews.jsx | 3 - .../support/FinancialProductDetailPage.jsx | 22 ++---- .../support/FinancialProductListPage.jsx | 6 +- src/pages/support/SupportItemPage.jsx | 2 +- src/pages/support/SupportListPage.jsx | 14 +++- src/pages/support/SupportRecommendPage.jsx | 2 +- src/pages/writeReview/components/Complete.jsx | 2 +- .../writeReview/components/WriteText.jsx | 6 +- 10 files changed, 119 insertions(+), 87 deletions(-) create mode 100644 src/hooks/useLikeToggle.js diff --git a/src/hooks/useLikeToggle.js b/src/hooks/useLikeToggle.js new file mode 100644 index 0000000..08cfb3a --- /dev/null +++ b/src/hooks/useLikeToggle.js @@ -0,0 +1,72 @@ +import { useState, useEffect } from "react"; +import { + getLikedCompanies, + likeCompany, + unlikeCompany, +} from "@/apis/company/getLikedCompanies"; +import useAuthStore from "@/store/authStore"; +import useLikeStore from "@/store/useLikeStore"; + +export const useLikeToggle = (companyId) => { + const [isLoggedIn, setIsLoggedIn] = useState(false); + const [isLiked, setIsLiked] = useState(false); + const [loading, setLoading] = useState(true); + const [showLoginModal, setShowLoginModal] = useState(false); + + const { likedMap, setLike } = useLikeStore(); + + // 로그인 및 좋아요 여부 확인 + useEffect(() => { + const check = async () => { + const authenticated = await useAuthStore.getState().checkAuth(); + setIsLoggedIn(authenticated); + + if (authenticated && companyId) { + const likedList = await getLikedCompanies(); + const liked = likedList.some( + (c) => String(c.companyId) === String(companyId), + ); + setIsLiked(liked); + setLike(companyId, liked); + } + setLoading(false); + }; + + if (companyId) check(); + }, [companyId]); + + // 좋아요 토글 함수 + const toggleLike = async () => { + const authenticated = await useAuthStore.getState().checkAuth(); + setIsLoggedIn(authenticated); + + if (!authenticated) { + setShowLoginModal(true); + return; + } + + try { + const currentLiked = likedMap[companyId] ?? false; + + if (currentLiked) { + await unlikeCompany(companyId); + } else { + await likeCompany(companyId); + } + const newLiked = !currentLiked; + setIsLiked(newLiked); + setLike(companyId, newLiked); + } catch (e) { + console.error("좋아요 토글 실패:", e); + } + }; + + return { + isLiked, + isLoggedIn, + loading, + toggleLike, + showLoginModal, + setShowLoginModal, + }; +}; diff --git a/src/pages/review/components/PlaceInfo.jsx b/src/pages/review/components/PlaceInfo.jsx index fca06ff..4f87c1a 100644 --- a/src/pages/review/components/PlaceInfo.jsx +++ b/src/pages/review/components/PlaceInfo.jsx @@ -3,70 +3,25 @@ import { companyTypeNameMap, companyTypeIconMap, } from "@constants/categoryMap"; -import { useState, useEffect } from "react"; import { usePaymentStore } from "@/store/paymentStore"; -import { - getLikedCompanies, - likeCompany, - unlikeCompany, -} from "@apis/company/getLikedCompanies"; import HaveToLoginModal from "@components/common/HaveToLoginModal"; import { openNaverMapRoute } from "@pages/map/utils/openNaverMapRoute"; import { useUserCoords } from "@pages/search/hooks/useUserCoords"; -import useAuthStore from "@/store/authStore"; +import Spinner from "@/components/common/Spinner"; +import { useLikeToggle } from "@/hooks/useLikeToggle"; const PlaceInfo = ({ placeInfo }) => { const userCoords = useUserCoords(); - - const [showLoginModal, setShowLoginModal] = useState(false); - const [isLiked, setIsLiked] = useState(false); - const [isLoggedIn, setIsLoggedIn] = useState(false); - const [loading, setLoading] = useState(true); - const { companyId } = usePaymentStore(); - // 로그인 여부 + 찜 여부 확인 - useEffect(() => { - const checkLoginAndLiked = async () => { - const isAuthenticated = await useAuthStore.getState().checkAuth(); - setIsLoggedIn(isAuthenticated); - - if (isAuthenticated && companyId) { - const likedList = await getLikedCompanies(); - const liked = likedList.some( - (c) => String(c.companyId) === String(companyId) - ); - setIsLiked(liked); - } - setLoading(false); - }; - - if (companyId) checkLoginAndLiked(); - }, [companyId]); - - const handleLikeClick = async () => { - const isAuthenticated = await useAuthStore.getState().checkAuth(); - setIsLoggedIn(isAuthenticated); - - if (!isAuthenticated) { - setShowLoginModal(true); - return; - } - - try { - setLoading(true); - if (isLiked) { - await unlikeCompany(companyId); - } else { - await likeCompany(companyId); - } - setIsLiked((prev) => !prev); - } catch (e) { - console.error("좋아요 토글 실패:", e); - } finally { - setLoading(false); - } - }; + const { + isLiked, + isLoggedIn, + loading, + toggleLike, + showLoginModal, + setShowLoginModal, + } = useLikeToggle(companyId); const handleRouteClick = (e) => { e.preventDefault(); @@ -89,6 +44,13 @@ const PlaceInfo = ({ placeInfo }) => { } }; + if (loading) { + return ( +
+ +
+ ); + } return (
@@ -153,7 +115,10 @@ const PlaceInfo = ({ placeInfo }) => { )}
diff --git a/src/pages/support/FinancialProductListPage.jsx b/src/pages/support/FinancialProductListPage.jsx index 74f988f..4d52ef9 100644 --- a/src/pages/support/FinancialProductListPage.jsx +++ b/src/pages/support/FinancialProductListPage.jsx @@ -75,7 +75,7 @@ const FinancialProductList = () => { const safeProducts = Array.isArray(sortedProducts) ? sortedProducts : []; return ( -
+

소비한 가치에 맞는 금융상품

{
-

- 총 {safeProducts.length}개 -

+

총 {safeProducts.length}개

{userName}님이 리뷰를 남긴 기업 특성과
연관된 금융상품 순으로 보여드려요! diff --git a/src/pages/support/SupportItemPage.jsx b/src/pages/support/SupportItemPage.jsx index 139825f..757c7d8 100644 --- a/src/pages/support/SupportItemPage.jsx +++ b/src/pages/support/SupportItemPage.jsx @@ -17,7 +17,7 @@ const SupportItemPage = () => { const dday = isDdayAvailable ? calculateDday(data.endDate) : null; return ( -

+
{isLoading && (
diff --git a/src/pages/support/SupportListPage.jsx b/src/pages/support/SupportListPage.jsx index 213bd2e..d34ce58 100644 --- a/src/pages/support/SupportListPage.jsx +++ b/src/pages/support/SupportListPage.jsx @@ -1,13 +1,23 @@ import FOAItem from "@/pages/support/components/FOAItem"; import { useNavigate } from "react-router-dom"; import { useGetAnnouncement } from "@/apis/announcement/queries"; +import Spinner from "@/components/common/Spinner"; const SupportListPage = () => { const navigate = useNavigate(); const { data, isLoading } = useGetAnnouncement(); + + if (isLoading) { + return ( +
+ +
+ ); + } + return ( -
+

진행 중인 지원사업

{
- {data.map((item, idx) => ( + {data?.map((item, idx) => ( ))}
diff --git a/src/pages/support/SupportRecommendPage.jsx b/src/pages/support/SupportRecommendPage.jsx index 62802e5..247ea11 100644 --- a/src/pages/support/SupportRecommendPage.jsx +++ b/src/pages/support/SupportRecommendPage.jsx @@ -33,7 +33,7 @@ const SupportRecommendPage = () => { }; return ( -
+
{
{/* 닫기 버튼 */}
navigate(`/review/${companyId}`)} > diff --git a/src/pages/writeReview/components/WriteText.jsx b/src/pages/writeReview/components/WriteText.jsx index a2019e1..dc9fc11 100644 --- a/src/pages/writeReview/components/WriteText.jsx +++ b/src/pages/writeReview/components/WriteText.jsx @@ -27,15 +27,15 @@ const WriteText = ({ onNext, onBack }) => { }; const handleClick = async () => { - setIsUploading(true); // 모달 띄우기 + setIsUploading(true); try { await postReview(reviewInfo, companyId, text); - setIsUploading(false); // 성공 시 닫기 + setIsUploading(false); onNext(); } catch (e) { console.log(e); - setIsUploading(false); // 실패 시도 닫기 + setIsUploading(false); fireToast("리뷰 등록에 실패했습니다.\n다시 시도해 주세요.", ErrorIcon); } }; From 098ca3b8afab961fbd017f33960e4b58e76c619d Mon Sep 17 00:00:00 2001 From: dbstj0403 Date: Tue, 20 May 2025 10:03:24 +0900 Subject: [PATCH 3/9] =?UTF-8?q?Feat:=20=EB=A7=88=EC=9D=B4=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=80=20=EB=A6=AC=EB=B7=B0=20=EB=AA=A9=EB=A1=9D=20?= =?UTF-8?q?=ED=9B=85=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/myPageDetail/MyPageDetailPage.jsx | 9 +++ .../myPageDetail/components/ReviewItem.jsx | 77 ++----------------- 2 files changed, 15 insertions(+), 71 deletions(-) diff --git a/src/pages/myPageDetail/MyPageDetailPage.jsx b/src/pages/myPageDetail/MyPageDetailPage.jsx index 78a2a54..970905f 100644 --- a/src/pages/myPageDetail/MyPageDetailPage.jsx +++ b/src/pages/myPageDetail/MyPageDetailPage.jsx @@ -6,6 +6,7 @@ import { useGetReviews, useGetCheers } from "@/apis/myPage/queries"; import ReviewItem from "@/pages/myPageDetail/components/ReviewItem"; import StoryItem from "@/pages/myPageDetail/components/StoryItem"; import noResult from "/svgs/myPage/noResult.svg"; +import Spinner from "@/components/common/Spinner"; const MyPageDetailPage = () => { const navigate = useNavigate(); @@ -20,6 +21,14 @@ const MyPageDetailPage = () => { }); // const { data: heartsData, isLoading: isLoadingHearts } = useGetHearts({ enabled: kind === "찜" }); + if (isLoadingReviews || isLoadingCheers) { + return ( +
+ +
+ ); + } + return (
navigate(-1)}> diff --git a/src/pages/myPageDetail/components/ReviewItem.jsx b/src/pages/myPageDetail/components/ReviewItem.jsx index 6ca3ffa..adc4b85 100644 --- a/src/pages/myPageDetail/components/ReviewItem.jsx +++ b/src/pages/myPageDetail/components/ReviewItem.jsx @@ -2,93 +2,28 @@ import ReviewContent from "@/pages/map/components/review/ReviewContent"; import { useNavigate } from "react-router-dom"; import { useGetCompanyPreview } from "@/apis/company/queries"; import { businessTypeNameMap } from "@/constants/categoryMap"; -import { useState, useEffect } from "react"; -import { - getLikedCompanies, - likeCompany, - unlikeCompany, -} from "@/apis/company/getLikedCompanies"; -import useAuthStore from "@/store/authStore"; + import useLikeStore from "@/store/useLikeStore"; -import HaveToLoginModal from "@/components/common/HaveToLoginModal"; import useUserInfoStore from "@/store/userInfoStore"; - -const dummyReviewItem = { - name: "김소영", - profileColor: "pink", - temperature: 74.6, - reviewContent: - "제품 품질도 후드러고 청건하게 잘 관리되어 있어요. 다시 방문하고 싶어요!", - reviewCategories: ["GOOD_QUALITY", "CLEAN", "REVISIT"], -}; +import { useLikeToggle } from "@/hooks/useLikeToggle"; const ReviewItem = ({ data }) => { const navigate = useNavigate(); const { data: companyData } = useGetCompanyPreview(data.companyId); - const [showLoginModal, setShowLoginModal] = useState(false); - const [isLiked, setIsLiked] = useState(false); - const [isLoggedIn, setIsLoggedIn] = useState(false); - const [loading, setLoading] = useState(true); const { userInfo } = useUserInfoStore(); - const { likedMap, setLike } = useLikeStore(); + const { likedMap } = useLikeStore(); const isGloballyLiked = likedMap[data.companyId] ?? false; + const { isLoggedIn, toggleLike } = useLikeToggle(data.companyId); + const combinedReviews = { ...data, name: userInfo.name, profileColor: userInfo.profileColor, }; - useEffect(() => { - const checkLoginAndLiked = async () => { - const isAuthenticated = await useAuthStore.getState().checkAuth(); - setIsLoggedIn(isAuthenticated); - - if (isAuthenticated && data?.companyId) { - const likedList = await getLikedCompanies(); - const liked = likedList.some( - (c) => String(c.companyId) === String(data.companyId) - ); - setIsLiked(liked); - setLike(data.companyId, liked); - } - setLoading(false); - }; - - if (data?.companyId) checkLoginAndLiked(); - }, [data?.companyId]); - - const handleLikeClick = async () => { - const isAuthenticated = await useAuthStore.getState().checkAuth(); - setIsLoggedIn(isAuthenticated); - - if (!isAuthenticated) { - setShowLoginModal(true); - return; - } - - try { - setLoading(true); - const currentLiked = likedMap[data.companyId] ?? false; - - if (currentLiked) { - await unlikeCompany(data.companyId); - } else { - await likeCompany(data.companyId); - } - - const newLiked = !currentLiked; - setIsLiked(newLiked); - setLike(data.companyId, newLiked); - } catch (e) { - console.error("좋아요 토글 실패:", e); - } finally { - setLoading(false); - } - }; - return (
{