diff --git a/app/(route)/(header)/plan/detail/[id]/page.tsx b/app/(route)/(header)/plan/detail/[id]/page.tsx index 35b6803..b455c4a 100644 --- a/app/(route)/(header)/plan/detail/[id]/page.tsx +++ b/app/(route)/(header)/plan/detail/[id]/page.tsx @@ -35,8 +35,11 @@ const PlanDetailsPage = ({ params }: PlanDetailsPageProps): ReactNode => { const { data, isPending, isError, error } = useQuery({ queryKey: ['plan', planId], queryFn: () => fetchPlan({ planId: planId, accessToken: session.data ? session.data.accessToken : null }), + enabled: session.data !== undefined, }) + // #2. Add Likes + let content if (isError) { // toast({ title: error.message }) diff --git a/components/main/MainPlanCard.tsx b/components/main/MainPlanCard.tsx index 334a6ca..01901a9 100644 --- a/components/main/MainPlanCard.tsx +++ b/components/main/MainPlanCard.tsx @@ -43,8 +43,6 @@ const MainPlanCard = ({ data }: MainPlanCardProps): ReactNode => { } = data const handleClick = () => { - console.log('plancard Isdone: ', isDone) - // Case1: 완성된 계획 if (isDone) { router.push(`${ROUTES.PLAN.DETAIL.url}/${id}`) diff --git a/components/plan/Cards.tsx b/components/plan/Cards.tsx index 544c497..50f52eb 100644 --- a/components/plan/Cards.tsx +++ b/components/plan/Cards.tsx @@ -167,8 +167,8 @@ export const PlaceCard = ({ data, focusedPlaceCard, handleClickCard }: PlaceCard {name} diff --git a/components/plan/details/Description.tsx b/components/plan/details/Description.tsx index ed354c5..cbdf411 100644 --- a/components/plan/details/Description.tsx +++ b/components/plan/details/Description.tsx @@ -1,17 +1,23 @@ 'use client' +import { useMutation } from '@tanstack/react-query' import Image from 'next/image' import { useRouter } from 'next/navigation' -import { ReactNode } from 'react' +import { ReactNode, useState } from 'react' import Backdrop from '@/components/common/Backdrop' import UserAvatar from '@/components/common/UserAvatar' import { PLAN_DEFAULT_IMAGE, USER_DEFAULT_IMAGE } from '@/lib/constants/dummy_data' +import { ClientModalData } from '@/lib/constants/errors' import { NO_USER_DESCRIPTION, NO_USER_NAME } from '@/lib/constants/no_data' import { ROUTES } from '@/lib/constants/routes' +import { queryClient } from '@/lib/HTTP/http' +import { planAddLikes, planAddScrap, planDeleteLikes, planDeleteScrap } from '@/lib/HTTP/plan/API' import LucideIcon from '@/lib/icons/LucideIcon' import { Plan } from '@/lib/types/Entity/plan' import { cn } from '@/lib/utils/cn' import { formatKoreanDate } from '@/lib/utils/dateUtils' +import useModal from '@/lib/utils/hooks/useModal' +import { toast } from '@/lib/utils/hooks/useToast' interface DescriptionProps { plan: Plan @@ -22,6 +28,9 @@ interface DescriptionProps { const Description = ({ plan, planUser, user, className }: DescriptionProps): ReactNode => { const router = useRouter() + // Modal Values + const { modalData, handleModalStates, Modal } = useModal() + console.log(plan) // #0. 계획 데이터 const { @@ -37,26 +46,73 @@ const Description = ({ plan, planUser, user, className }: DescriptionProps): Rea scrapCnt, isScraped, comments, + id, } = plan + const [tmpIsLiked, setTmpIsLiked] = useState(isLiked) + const [tmpIsScrap, setTmpIsScrap] = useState(isScraped) + + const onConfirm = () => { + if (modalData.id === 'confirm') { + switch (modalData) { + case ClientModalData.loginRequiredError: + router.push(ROUTES.AUTH.LOGIN.url) + break + + default: + break + } + } + } + + // #1. Plan Likes Mutation + const { mutate: likesMutate } = useMutation({ + mutationKey: ['plan', 'likes', { planId: id }], + mutationFn: !tmpIsLiked ? planAddLikes : planDeleteLikes, + onSuccess: () => { + setTmpIsLiked(prev => !prev) + toast({ title: '변경 완료!' }) + }, + onError: () => { + setTmpIsLiked(prev => !prev) + }, + onSettled: () => { + queryClient.invalidateQueries({ queryKey: ['plan', 'likes', { planId: id }] }) + }, + }) - /** - * 좋아요 클릭 함수 - */ const likeHandler = () => { // #1. 로그인X 상태 (좋아요 누르기, 스크랩 누르기) if (!user) { - router.push(ROUTES.AUTH.LOGIN.url) + handleModalStates(ClientModalData.loginRequiredError, 'open') } // #2. 로그인 상태 + likesMutate({ planId: id, accessToken: user.accessToken }) } + // #1. Plan Scrap Mutations + const { mutate: scrapMutate } = useMutation({ + mutationKey: ['plan', 'scrap', { planId: id }], + mutationFn: !tmpIsScrap ? planAddScrap : planDeleteScrap, + onSuccess: () => { + setTmpIsScrap(prev => !prev) + toast({ title: '변경 완료!' }) + }, + onError: () => { + setTmpIsScrap(prev => !prev) + }, + onSettled: () => { + queryClient.invalidateQueries({ queryKey: ['plan', 'scrap', { planId: id }] }) + }, + }) const scrapHandler = () => { // #1. 로그인X 상태 (좋아요 누르기, 스크랩 누르기) if (!user) { - router.push(ROUTES.AUTH.LOGIN.url) + handleModalStates(ClientModalData.loginRequiredError, 'open') } // #2. 로그인 상태 + scrapMutate({ planId: id, accessToken: user.accessToken }) } + return (
@@ -93,20 +149,34 @@ const Description = ({ plan, planUser, user, className }: DescriptionProps): Rea {budget}원
-
- - {likeCnt} -
-
- +
+ {comments?.length}
-
- +
+ + {likeCnt} +
+
+ {scrapCnt}
+
) } diff --git a/components/plan/details/Schedule.tsx b/components/plan/details/Schedule.tsx index ba5df55..96fe0ce 100644 --- a/components/plan/details/Schedule.tsx +++ b/components/plan/details/Schedule.tsx @@ -155,7 +155,7 @@ const UniSchedule = ({ schedule, fillIndex, date }: ScheduleProps): ReactNode => isReduced={false} className='h-[200px]' /> -
추가된 여행지가 없습니다!
+
추가된 여행지가 없습니다!
) } diff --git a/lib/HTTP/plan/API.ts b/lib/HTTP/plan/API.ts index 4f1a018..feb33ce 100644 --- a/lib/HTTP/plan/API.ts +++ b/lib/HTTP/plan/API.ts @@ -149,6 +149,7 @@ export const fetchPlan = async ({ planId, accessToken }: FetchPlanProps) => { ] const API = BACKEND_ROUTES.PLAN.GET + console.log('accessToken') const res = await fetch(`${attachQuery(`/server/${API.url}`, queries)}`, { method: API.method, @@ -246,6 +247,8 @@ export const fetchPlan = async ({ planId, accessToken }: FetchPlanProps) => { isLiked: liked, } + console.log('PlanData from fetchplans: ', planData) + // 계획을 만든 사람의 정보 const planUser = { userId: user.userId as number, @@ -253,9 +256,6 @@ export const fetchPlan = async ({ planId, accessToken }: FetchPlanProps) => { status_message: user.status_message as string, image: user.image as string, } - console.log('planData:', planData) - console.log('PlanUser:', planUser) - console.log('Tags:', tags) return { planData, planUser, tags } } @@ -362,15 +362,18 @@ interface PlanDeleteScrapType { export const planDeleteScrap = async ({ planId, accessToken }: PlanDeleteScrapType) => { const Route = BACKEND_ROUTES.PLAN.SCRAP.DELETE - const res = await fetch(`/server/${Route.url}`, { + const queries: Queries = [ + { + key: 'planId', + value: planId, + }, + ] + const res = await fetch(`${attachQuery(`/server/${Route.url}`, queries)}`, { method: Route.method, headers: { Authorization: accessToken, 'Content-Type': 'application/json', }, - body: JSON.stringify({ - planId: planId, - }), credentials: 'include', }) if (!res.ok) { @@ -391,6 +394,8 @@ interface PlanAddLikesType { accessToken: string } export const planAddLikes = async ({ planId, accessToken }: PlanAddLikesType) => { + console.log('planaddlikes executed') + const Route = BACKEND_ROUTES.PLAN.LIKE.ADD const res = await fetch(`/server/${Route.url}`, { @@ -424,15 +429,19 @@ interface PlanDeleteLikesType { export const planDeleteLikes = async ({ planId, accessToken }: PlanDeleteLikesType) => { const Route = BACKEND_ROUTES.PLAN.LIKE.DELETE - const res = await fetch(`/server/${Route.url}`, { + const queries: Queries = [ + { + key: 'planId', + value: planId, + }, + ] + + const res = await fetch(`${attachQuery(`/server/${Route.url}`, queries)}`, { method: Route.method, headers: { Authorization: accessToken, 'Content-Type': 'application/json', }, - body: JSON.stringify({ - planId: planId, - }), credentials: 'include', }) if (!res.ok) {