Skip to content

Commit

Permalink
feat: 디테일 페이지 스크랩/좋아요 기능 추가
Browse files Browse the repository at this point in the history
  • Loading branch information
jihostudy committed Oct 1, 2024
1 parent ca3e32e commit eeeaa7f
Show file tree
Hide file tree
Showing 6 changed files with 110 additions and 30 deletions.
3 changes: 3 additions & 0 deletions app/(route)/(header)/plan/detail/[id]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 })
Expand Down
2 changes: 0 additions & 2 deletions components/main/MainPlanCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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}`)
Expand Down
4 changes: 2 additions & 2 deletions components/plan/Cards.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -167,8 +167,8 @@ export const PlaceCard = ({ data, focusedPlaceCard, handleClickCard }: PlaceCard
<span className='truncate text-base font-semibold group-hover:text-tbBlue'>{name}</span>
<LucideIcon
name='Bookmark'
className={cn('absolute right-2 top-1', tmpIsScrap ? 'hover:fill-none' : 'hover:fill-tbRed')}
fill={tmpIsScrap ? 'tbRed' : undefined}
className={cn('absolute right-2 top-1', tmpIsScrap ? 'hover:fill-none' : 'hover:fill-tbPrimaryHover')}
fill={tmpIsScrap ? 'tbPrimaryHover' : undefined}
onClick={scrapHandler}
/>
</div>
Expand Down
98 changes: 84 additions & 14 deletions components/plan/details/Description.tsx
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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 {
Expand All @@ -37,26 +46,73 @@ const Description = ({ plan, planUser, user, className }: DescriptionProps): Rea
scrapCnt,
isScraped,
comments,
id,
} = plan
const [tmpIsLiked, setTmpIsLiked] = useState<boolean>(isLiked)
const [tmpIsScrap, setTmpIsScrap] = useState<boolean>(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 (
<div className={cn('relative flex cursor-pointer items-start justify-start gap-6 px-3 py-6', className)}>
<div className='group relative aspect-video h-full origin-left'>
Expand Down Expand Up @@ -93,20 +149,34 @@ const Description = ({ plan, planUser, user, className }: DescriptionProps): Rea
<span>{budget}</span>
</div>
<div className='flex items-center justify-start gap-3'>
<div className='flex w-fit items-center justify-start gap-1 text-base'>
<LucideIcon name='Heart' fill={isLiked ? 'tbRed' : undefined} strokeWidth={isLiked ? 0 : 2} />
<span>{likeCnt}</span>
</div>
<div onClick={likeHandler} className='flex w-fit items-center justify-start gap-1 text-base'>
<LucideIcon name='MessageCircle' />
<div className='flex w-fit items-center justify-start gap-1 text-lg'>
<LucideIcon name='MessageCircle' size={20} />
<span>{comments?.length}</span>
</div>
<div onClick={scrapHandler} className='flex w-fit items-center justify-start gap-1 text-base'>
<LucideIcon name='Bookmark' fill={isScraped ? 'tbRed' : undefined} />
<div className='flex w-fit items-center justify-start gap-1 text-lg'>
<LucideIcon
onClick={likeHandler}
name='Heart'
fill={tmpIsLiked ? 'tbRed' : undefined}
// strokeWidth={tmpIsLiked ? 0 : 2}
size={20}
className={cn(tmpIsScrap ? 'hover:fill-none' : 'hover:fill-tbRed')}
/>
<span>{likeCnt}</span>
</div>
<div className='flex w-fit items-center justify-start gap-1 text-lg'>
<LucideIcon
onClick={scrapHandler}
name='Bookmark'
className={cn(tmpIsScrap ? 'hover:fill-none' : 'hover:fill-tbPrimaryHover')}
fill={isScraped ? 'tbPrimaryHover' : undefined}
size={20}
/>
<span>{scrapCnt}</span>
</div>
</div>
</div>
<Modal onConfirm={onConfirm} />
</div>
)
}
Expand Down
2 changes: 1 addition & 1 deletion components/plan/details/Schedule.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ const UniSchedule = ({ schedule, fillIndex, date }: ScheduleProps): ReactNode =>
isReduced={false}
className='h-[200px]'
/>
<div className='flex h-20 w-full items-center justify-center'>추가된 여행지가 없습니다!</div>
<div className='flex w-full items-center justify-center'>추가된 여행지가 없습니다!</div>
</>
)
}
Expand Down
31 changes: 20 additions & 11 deletions lib/HTTP/plan/API.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -246,16 +247,15 @@ export const fetchPlan = async ({ planId, accessToken }: FetchPlanProps) => {
isLiked: liked,
}

console.log('PlanData from fetchplans: ', planData)

// 계획을 만든 사람의 정보
const planUser = {
userId: user.userId as number,
username: user.username as string,
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 }
}
Expand Down Expand Up @@ -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) {
Expand All @@ -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}`, {
Expand Down Expand Up @@ -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) {
Expand Down

0 comments on commit eeeaa7f

Please sign in to comment.