diff --git a/src/components/Common/CompleteTrip.tsx b/src/components/Common/CompleteTrip.tsx index d989162..6a1a749 100644 --- a/src/components/Common/CompleteTrip.tsx +++ b/src/components/Common/CompleteTrip.tsx @@ -12,8 +12,8 @@ export default function CompleteTrip({ planDetail, closeModal }: CompleteTripPro alert("여행이 완료되었습니다!"); window.location.reload(); closeModal(); - } catch (error) { - console.error(`여행 완료에 실패 하였습니다. ${error}`); + } catch (error: any) { + alert(error.message); } } diff --git a/src/components/MyPlans/Cards/QuotationCard.tsx b/src/components/MyPlans/Cards/QuotationCard.tsx index 6cac8bd..1276e81 100644 --- a/src/components/MyPlans/Cards/QuotationCard.tsx +++ b/src/components/MyPlans/Cards/QuotationCard.tsx @@ -11,7 +11,6 @@ import { convertRegionToKorean } from "@/utils/formatRegion"; import { useRouter } from "next/router"; import { useState, useEffect } from "react"; import ModalLayout from "@/components/Common/ModalLayout"; -import ChargeModal from "@/components/Gnb/ChargeModal"; interface MakerInfo { nickName: string; @@ -43,7 +42,6 @@ interface QuotationCardProps { export default function QuotationCard({ quotationDetail, planDetail }: QuotationCardProps) { const router = useRouter(); const [isModalOpen, setIsModalOpen] = useState(false); - const [isChargeModalOpen, setIsChargeModalOpen] = useState(false); const [coconut, setCoconut] = useState(0); useEffect(() => { @@ -63,7 +61,7 @@ export default function QuotationCard({ quotationDetail, planDetail }: Quotation await QuotationServiceDreamer.confirmQuotation({ isConfirmed: true }, quotationDetail.id); alert("견적이 확정되었습니다."); window.location.reload(); - router.push(`/mytrip-manage/ongoin-plan/detail/${planDetail.id}`); + router.push(`/mytrip-manage/ongoing-plan/detail/${planDetail.id}`); } catch (error: any) { if (error.status === 400) { setIsModalOpen(true); @@ -214,9 +212,6 @@ export default function QuotationCard({ quotationDetail, planDetail }: Quotation - {isChargeModalOpen && ( - - )} ); } diff --git a/src/pages/mytrip-manage/quotationdetail-dreamer/[planId].tsx b/src/pages/mytrip-manage/quotationdetail-dreamer/[planId].tsx index 52af0aa..9f09e95 100644 --- a/src/pages/mytrip-manage/quotationdetail-dreamer/[planId].tsx +++ b/src/pages/mytrip-manage/quotationdetail-dreamer/[planId].tsx @@ -18,6 +18,7 @@ import { Plan } from "@/services/planService"; import { useState } from "react"; import withAuthAccess from "@/stores/withAuthAccess"; import ShareSNS from "@/components/Common/ShareSNS"; +import ModalLayout from "@/components/Common/ModalLayout"; interface MakerInfo { nickName: string; @@ -42,35 +43,13 @@ interface QuotationDetail { } export function QuotationDetailDreamer() { - // useEffect(() => { - // if (typeof window !== "undefined" && window.Kakao) { - // const Kakao = window.Kakao; - // if (!Kakao.isInitialized()) { - // Kakao.init("0337a68dec8e9d5ebea78113c3b9fc62"); - // } - // } - // }, []); - // //init괄호 안에는 카카오디벨로퍼스에서 받은 javascript키 입력 - - // const shareMessage = () => { - // if (typeof window !== "undefined" && window.Kakao) { - // const Kakao = window.Kakao; - // Kakao.Share.sendScrap({ - // requestUrl: "http://localhost:3000", - // }); - // } - // }; - // //requestUrl부분에 사용할 도메인 입력(현재 테스트중으로 로컬도메인) - - // const btnShareFb = () => { - // const pageUrl = "http://localhost:3000"; - // window.open(`http://www.facebook.com/sharer/sharer.php?u=${pageUrl}`); - // }; - const router = useRouter(); const { planId, quotationId } = router.query; const [planDetail, setPlanDetail] = useState(null); const [quotationDetail, setQuotationDetail] = useState(null); + const [isModalOpen, setIsModalOpen] = useState(false); + const [coconut, setCoconut] = useState(0); + const fetchQuotations = async (planId: string) => { try { const response = await QuotationServiceDreamer.getQuotations({ planId }); @@ -122,12 +101,30 @@ export function QuotationDetailDreamer() { try { if (quotationDetail) await QuotationServiceDreamer.confirmQuotation({ isConfirmed: true }, quotationDetail.id); - alert("견적확정 확정되었습니다."); - } catch (error) { - alert(`견적확정에 실패했습니다. 다시 시도해주세요. ${error}`); + alert("견적이 확정되었습니다."); + window.location.reload(); + router.push(`/mytrip-manage/ongoing-plan/detail/${planId}`); + } catch (error: any) { + if (error.status === 400) { + setIsModalOpen(true); + } else { + alert(`견적 확정에 실패했습니다. 다시 시도해주세요. ${error.message}`); + } } } + useEffect(() => { + const authData = localStorage.getItem("auth"); + if (authData) { + try { + const parsedData = JSON.parse(authData); + setCoconut(parsedData.state?.coconut ?? 0); + } catch (error) { + console.error("로컬스토리지 데이터 파싱 오류:", error); + } + } + }, []); + useEffect(() => { if (!window.Kakao.isInitialized()) { window.Kakao.init(process.env.NEXT_PUBLIC_KAKAO_API_KEY); @@ -217,147 +214,168 @@ export function QuotationDetailDreamer() { ); } return ( -
-
- 견적 상세 -
-
-
-
-
-
- {quotationDetail.isConfirmed !== false && ( -
-
-
- 프로필사진 +
+
+
+ 견적 상세 +
+
+
+
+
+
+ {quotationDetail.isConfirmed !== false && ( +
-
-
-

- {quotationDetail.maker.nickName} -

-
-
- 별점 -

{quotationDetail.maker.averageRating}

-

- ({quotationDetail.maker.totalReviews}) -

-
-

-
- - 링크이미지 -

SNS

- -
-

-
-

{quotationDetail.maker.totalConfirms}

-

확정

-
-
-
-
+
+
좋아요 -

{quotationDetail.maker.totalFollows}

+
+
+
+

+ {quotationDetail.maker.nickName} +

+
+
+ 별점 +

{quotationDetail.maker.averageRating}

+

+ ({quotationDetail.maker.totalReviews}) +

+
+

+
+ + 링크이미지 +

SNS

+ +
+

+
+

{quotationDetail.maker.totalConfirms}

+

확정

+
+
+
+
+ 좋아요 +

{quotationDetail.maker.totalFollows}

+
-
-
-

- {quotationDetail.content} -

+
+

+ {quotationDetail.content} +

+
-
-
-
-

- 견적 코코넛 -

-

- {quotationDetail.price} 개 -

-
-
-
-

플랜 공유하기

-
{sharePromptContent}
-
-
-
-

- 플랜 정보 -

- - {planDetail.status === "PENDING" && ( -
-
- 알림 확정하지 않은 플랜이에요! +
+
+

+ 견적 코코넛 +

+

+ {quotationDetail.price} 개 +

+
+
+
+

플랜 공유하기

+
{sharePromptContent}
+
+
+
+

+ 플랜 정보 +

+ + {planDetail.status === "PENDING" && ( +
+
+ 알림 확정하지 않은 플랜이에요! +
-
- )} + )} +
-
-
- {planDetail.status === "PENDING" && ( -
- + - + +
+ )} + {planDetail.status === "PENDING" && ( +
+ )} +
+

플랜 공유하기

+
{sharePromptContent}
- )} - {planDetail.status === "PENDING" && ( -
- )} -
-

플랜 공유하기

-
{sharePromptContent}
+ {isModalOpen && ( + setIsModalOpen(false)}> +
+
+
+

보유 코코넛 :

{coconut}

{" "} +

+
+
+

필요 코코넛 :

+

+ {quotationDetail.price.toLocaleString()} +

+

+
+
+
+
+ )}
); } diff --git a/src/services/planService.ts b/src/services/planService.ts index 2662cad..7e6a681 100644 --- a/src/services/planService.ts +++ b/src/services/planService.ts @@ -1,4 +1,4 @@ -import { BAD_REQUEST, CONFLICT } from "@/utils/errorStatus"; +import { BAD_REQUEST, CONFLICT, NOT_FOUND, FORBIDDEN } from "@/utils/errorStatus"; import { api } from "./api"; import { ServiceArea } from "@/utils/formatRegion"; @@ -95,9 +95,8 @@ const planService = { } return response; - } catch (error) { - console.error("여행 조회 실패", error); - throw error; + } catch (error: any) { + throw error("플랜 목록을 불러오는데 실패했습니다."); } }, @@ -110,8 +109,7 @@ const planService = { window.location.href = "/404"; return Promise.reject(new Error("해당 여행 플랜을 찾을 수 없습니다.")); } - console.error("여행 데이터 요청 실패", error); - throw error; + throw error("여행 데이터를 불러오는데 실패했습니다."); } }, @@ -158,8 +156,8 @@ const planService = { `/plans/dreamer?readyToComplete=true${queryString}`, ); return response; - } catch (error) { - console.error("완료 플랜 조회 실패", error); + } catch (error: any) { + throw error("완료 플랜을 불러오는데 실패했습니다."); } }, @@ -171,8 +169,8 @@ const planService = { `/plans/dreamer?reviewed=false${queryString}`, ); return response; - } catch (error) { - console.error("리뷰 작성 가능 플랜 조회 실패", error); + } catch (error: any) { + throw error("리뷰 가능 플랜을 불러오는데 실패했습니다."); } }, @@ -180,8 +178,12 @@ const planService = { try { const response = await api.delete(`/plans/${planId}`); return response; - } catch (error) { - console.error("플랜 취소 실패", error); + } catch (error: any) { + if (error.response?.status === BAD_REQUEST) { + throw new Error("진행중인 플랜은 삭제할 수 없습니다."); + } else if (error.response?.status === FORBIDDEN) { + throw new Error("해당 플랜을 삭제할 권한이 없습니다."); + } } }, @@ -191,20 +193,17 @@ const planService = { console.log("응답 데이터:", response); return response; } catch (error: any) { - if (error.response) { + if (error.response?.status === BAD_REQUEST) { // 서버에서 응답이 왔을 때 (500, 404 등) - console.error("Server Error:", error.response.status, error.response.data); - throw new Error( - `API Error: ${error.response.status} - ${error.response.data.message || "Unknown error"}`, - ); + throw new Error("플랜 완료 요청은 진행중(확정견적이 있는) 상태일 때만 가능합니다."); + } else if (error.response?.status === NOT_FOUND) { + throw new Error("해당 플랜을 찾을 수 없습니다."); } else if (error.request) { // 요청은 갔지만 응답이 없을 때 (네트워크 문제) - console.error("No Response:", error.request); - throw new Error("No response from server. Please try again."); + throw new Error("서버로부터 응답이 없습니다. 다시 시도해 주세요."); } else { // 요청 자체의 문제 (설정 오류 등) - console.error("Request Error:", error.message); - throw new Error("Request setup error."); + throw new Error("요청 오류 입니다."); } } }, diff --git a/src/services/quotationServiceDreamer.ts b/src/services/quotationServiceDreamer.ts index 3609eb9..a577d43 100644 --- a/src/services/quotationServiceDreamer.ts +++ b/src/services/quotationServiceDreamer.ts @@ -1,3 +1,4 @@ +import { FORBIDDEN } from "@/utils/errorStatus"; import { api } from "./api"; interface MakerInfo { @@ -57,19 +58,23 @@ export const QuotationServiceDreamer = { } return response; - } catch (error) { - console.error("견적 목록 조회 실패", error); - throw error; + } catch (error: any) { + if (error.response && error.response?.status === FORBIDDEN) { + throw new Error("해당 견적서의 Dreamer만 조회할 수 있습니다."); + } + throw new Error("견적서를 불러 오는 중 오류가 발생했습니다."); } }, getQuotationDetail: async (planId: string): Promise => { try { - const response = await api.get(`/plans/${planId}/qoutes`); + const response = await api.get(`/plans/${planId}/quotes`); return response; - } catch (error) { - console.error("견적 상세 조회 실패", error); - throw error; + } catch (error: any) { + if (error.response && error.response?.status === FORBIDDEN) { + throw new Error("해당 견적서의 Maker와 Dreamer만 조회할 수 있습니다."); + } + throw new Error("견적서를 불러 오는 중 오류가 발생했습니다."); } }, @@ -78,15 +83,8 @@ export const QuotationServiceDreamer = { const response = await api.patch(`quotes/${quoteId}/confirm`, payload); return response; } catch (error: any) { - if (error.response) { - // 서버에서 응답이 왔을 때 (500, 404 등) - throw error.response; - } else if (error.request) { - // 요청은 갔지만 응답이 없을 때 (네트워크 문제) - throw new Error("No response from server. Please try again."); - } else { - // 요청 자체의 문제 (설정 오류 등) - throw new Error("Request setup error."); + if (error.response && error.response?.status === FORBIDDEN) { + throw new Error("해당 견적서의 Dreamer만 확정할 수 있습니다."); } } },