diff --git a/apps/jurumarble/src/app/vote/[id]/components/Comment.tsx b/apps/jurumarble/src/app/vote/[id]/components/Comment.tsx index f35c62f2..a775e0b8 100644 --- a/apps/jurumarble/src/app/vote/[id]/components/Comment.tsx +++ b/apps/jurumarble/src/app/vote/[id]/components/Comment.tsx @@ -1,4 +1,5 @@ import { useOutsideClick, useToggle } from "@monorepo/hooks"; +import ModifyDeleteButtonBox from "app/vote/components/MenuBox"; import Image from "next/image"; import { ExImg1 } from "public/images"; import React from "react"; @@ -54,20 +55,20 @@ function Comment({ comment, mutateDeleteComment, mutateLike, mutateHate }: Props {mbti} */} 여 | 20대 | INTJ | 10병 - {nickName} + {nickName} {content}
{createdDate.slice(0, 10)}
・ - ❤️ 좋아요 {likeCount} ・ - 🖤 싫어요 {hateCount}{" "} + ❤️ 좋아요 {likeCount ?? 0} ・ + 🖤 싫어요 {hateCount ?? 0}{" "}
- {/* {toggleMenu && } */} + {toggleMenu && } {toggleWarningModal && ( )} @@ -95,7 +96,7 @@ const ContentsBox = styled.div` `; const Contents = styled.div` - padding: 8px 0 8px 0; + padding: 12px 0 12px 0; display: flex; align-items: center; ${({ theme }) => css` @@ -127,6 +128,7 @@ const DivideTag = styled.div` `; const NickName = styled.div` + padding-top: 6px; font-weight: 700; ${({ theme }) => css` color: ${theme.colors.black_02}; diff --git a/apps/jurumarble/src/app/vote/[id]/components/CommentContainer.tsx b/apps/jurumarble/src/app/vote/[id]/components/CommentContainer.tsx index 27d5d24c..07c0c701 100644 --- a/apps/jurumarble/src/app/vote/[id]/components/CommentContainer.tsx +++ b/apps/jurumarble/src/app/vote/[id]/components/CommentContainer.tsx @@ -1,7 +1,11 @@ +import { useQueryClient } from "@tanstack/react-query"; +import { queryKeys } from "lib/queryKeys"; import Link from "next/link"; +import { useState } from "react"; import styled from "styled-components"; -import useCommentFilter from "../hooks/useCommentFilter"; +// import useCommentFilter from "../hooks/useCommentFilter"; +import useCommentServices from "../services/useCommentServices"; import Comment from "./Comment"; import CommentForm from "./CommentForm"; @@ -12,39 +16,97 @@ interface Props { } function CommentContainer({ postId }: Props) { - const { commentFilter, onChangeCommentFilter } = useCommentFilter(); + const queryClient = useQueryClient(); + const [sortBy, setSortBy] = useState<"ByTime" | "ByPopularity">("ByTime"); + const onChangeFilter = (sort: "ByTime" | "ByPopularity") => { + setSortBy(sort); + }; + + // const { commentFilter, onChangeCommentFilter } = useCommentFilter(); + const { comments, isError, isLoading, mutateHate, mutateLike, mutateComment } = + useCommentServices(postId, sortBy, "votes"); + + const [commentForm, setCommentForm] = useState(""); + const onChangeCommentForm = (e: React.ChangeEvent) => { + setCommentForm(e.target.value); + }; + const onSubmitComment = () => { + mutateComment( + { + content: commentForm, + parentId: null, + }, + { + onSuccess: () => { + queryClient.invalidateQueries([queryKeys.DETAIL_COMMENT_LIST]); + setCommentForm(""); + }, + }, + ); + }; + + if (isError) return
에러
; + if (!comments) return
데이터 없음
; + + const commentList = comments.pages.flatMap((page) => page.content); + return ( - - - {/* */} - - void 0} - mutateLike={() => void 0} - mutateHate={() => void 0} - key={`comment_id`} - /> -
-
+ <> + + + + + {!isLoading && + commentList.map( + ( + { + id, + age, + content, + createdDate, + gender, + hateCount, + imageUrlstring, + likeCount, + mbti, + nickName, + userId, + }, + index, + ) => ( + void 0} + mutateLike={() => mutateLike(id)} + mutateHate={() => mutateHate(id)} + key={`comment_id_${index}`} + /> + ), + )} +
+
+ + ); } @@ -55,21 +117,11 @@ const Container = styled.div` gap: 16px; `; -const DetailButton = styled.button` - position: absolute; - bottom: -50px; - right: 50%; - transform: translateX(50%); -`; - -const DetailButtonInner = styled.div` +const BgContainer = styled.div` + background-color: ${({ theme }) => theme.colors.bg_02}; width: 100%; - display: flex; - align-items: center; - justify-content: center; - gap: 7px; - padding-right: 4px; - font-size: 14px; + height: 76px; + padding-bottom: 50px; `; export default CommentContainer; diff --git a/apps/jurumarble/src/app/vote/[id]/components/CommentForm.tsx b/apps/jurumarble/src/app/vote/[id]/components/CommentForm.tsx index abe58f2f..981eb60f 100644 --- a/apps/jurumarble/src/app/vote/[id]/components/CommentForm.tsx +++ b/apps/jurumarble/src/app/vote/[id]/components/CommentForm.tsx @@ -4,22 +4,22 @@ import { ExImg1 } from "public/images"; import React from "react"; import styled, { css } from "styled-components"; -interface Props {} +interface Props { + commentForm: string; + onChangeCommentForm(e: React.ChangeEvent): void; + onSubmitComment(): void; +} -function CommentForm() { +function CommentForm({ commentForm, onChangeCommentForm, onSubmitComment }: Props) { const onSubmit = (e: React.FormEvent) => { e.preventDefault(); - // onSubmitComment(); + onSubmitComment(); }; return (
- + 등록
diff --git a/apps/jurumarble/src/app/vote/[id]/components/CommentToolbar.tsx b/apps/jurumarble/src/app/vote/[id]/components/CommentToolbar.tsx index 9d9c1bcb..74ce396c 100644 --- a/apps/jurumarble/src/app/vote/[id]/components/CommentToolbar.tsx +++ b/apps/jurumarble/src/app/vote/[id]/components/CommentToolbar.tsx @@ -3,23 +3,24 @@ import styled, { css } from "styled-components"; interface Props { commentCount: number; - sortBy?: string; - onChangeFilter: (sort: string) => void; + sortBy: "ByTime" | "ByPopularity"; + onChangeFilter: (sort: "ByTime" | "ByPopularity") => void; } function CommentToolBar({ commentCount, onChangeFilter, sortBy }: Props) { + console.log("sortBy", sortBy); return ( 댓글 <span className="point">{commentCount}</span> - - diff --git a/apps/jurumarble/src/app/vote/[id]/components/VoteDescription.tsx b/apps/jurumarble/src/app/vote/[id]/components/VoteDescription.tsx index d9ecba5a..b2ea5e6a 100644 --- a/apps/jurumarble/src/app/vote/[id]/components/VoteDescription.tsx +++ b/apps/jurumarble/src/app/vote/[id]/components/VoteDescription.tsx @@ -1,7 +1,10 @@ +import { postExecuteVote } from "lib/apis/vote"; import Image, { StaticImageData } from "next/image"; +import { useParams } from "next/navigation"; import React from "react"; import SvgIcPrev from "src/assets/icons/components/IcPrev"; import styled, { css } from "styled-components"; +import useExecuteVoteService from "../services/useExecuteVoteService"; type AorB = "A" | "B"; type ActiveType = "active" | "inactive" | null; @@ -32,6 +35,9 @@ function VoteDescription({ totalCountB, onMutateVoting, }: Props) { + const params = useParams(); + console.log(select); + const getAB = (direction: Direction) => { return direction === "left" ? "A" : "B"; }; @@ -57,9 +63,12 @@ function VoteDescription({ A 이미지
@@ -75,7 +84,17 @@ function VoteDescription({ onClick={() => onClickVote("B")} percent={percentageB > 0 ? percentageB : 0} > - B 이미지 + B 이미지
{titleB} {percentageB}% @@ -93,7 +112,9 @@ function VoteDescription({ ); } -const Container = styled.div``; +const Container = styled.div` + overflow: hidden; +`; const ImageWrapper = styled.div` position: relative; @@ -126,13 +147,15 @@ const FlexRow = styled.div` const variantStyles = { active: css` transition: all 0.3s ease-in-out; - width: 100%; + width: 90%; font-size: 16px; font-weight: 700; padding: 0 1px; + pointer-events: none; `, inactive: css` - width: 0%; + width: 10%; + pointer-events: none; `, }; @@ -147,6 +170,7 @@ const LeftVote = styled.div<{ selected: ActiveType; percent: number }>` aspect-ratio: 1; max-height: 300px; display: flex; + transition: all 0.3s ease-in-out; justify-content: center; .overlay { position: absolute; @@ -164,6 +188,7 @@ const LeftVote = styled.div<{ selected: ActiveType; percent: number }>` background: rgba(250, 94, 45, 0.7); border-radius: 10px; border: 2px solid #ff4a16; + ${({ selected, percent }) => selected === "active" && css` @@ -172,6 +197,9 @@ const LeftVote = styled.div<{ selected: ActiveType; percent: number }>` `}; } ${({ selected }) => typeGuardVariantStyle(selected)} + &:hover { + width: 90%; + } `; const RightVote = styled(LeftVote)` diff --git a/apps/jurumarble/src/app/vote/[id]/hooks/useCommentFilter.ts b/apps/jurumarble/src/app/vote/[id]/hooks/useCommentFilter.ts deleted file mode 100644 index 150f6e69..00000000 --- a/apps/jurumarble/src/app/vote/[id]/hooks/useCommentFilter.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { useState } from "react"; - -export interface CommentFilter { - age: number | null; - mbti: string | null; - gender: string | null; - sortBy: string | null; -} - -export default function useCommentFilter() { - const [commentFilter, setCommentFilter] = useState({ - age: null, - mbti: null, - gender: null, - sortBy: "ByTime", - }); - - const onChangeCommentFilter = (sort: string) => { - setCommentFilter((prev) => ({ - ...prev, - sortBy: sort, - })); - }; - - return { commentFilter, onChangeCommentFilter }; -} diff --git a/apps/jurumarble/src/app/vote/[id]/page.tsx b/apps/jurumarble/src/app/vote/[id]/page.tsx index 4d50b601..a92b4f0c 100644 --- a/apps/jurumarble/src/app/vote/[id]/page.tsx +++ b/apps/jurumarble/src/app/vote/[id]/page.tsx @@ -9,19 +9,38 @@ import VoteDescription from "./components/VoteDescription"; import { useState } from "react"; import ChipContainer from "./components/ChipContainer"; import CommentContainer from "./components/CommentContainer"; -import { useSearchParams } from "next/navigation"; +import { useParams, useSearchParams } from "next/navigation"; import { useToggle } from "@monorepo/hooks"; import SearchRestaurantModal from "./components/SearchRestaurantModal"; +import usePostBookmarkService from "../post/services/useBookmarkService"; +import useVoteLoadService from "./services/useVoteLoadService"; +import useExecuteVoteService from "./services/useExecuteVoteService"; function Detail() { - const searchParams = useSearchParams(); - const postId = searchParams.get("id"); + const params = useParams(); + + const postId = params.id; const [selected, setSelected] = useState<"A" | "B" | null>(null); + + const [isSearchRestaurantModal, onToggleSearchRestaurantModal] = useToggle(true); + + const { data, isError, isLoading } = useVoteLoadService(Number(postId)); + + const { mutateBookMark, bookMarkCheckQuery } = usePostBookmarkService(Number(postId)); + + const { mutate, select } = useExecuteVoteService(Number(postId)); const onMutateVoting = (select: "A" | "B") => { - setSelected(select); + mutate(select); }; - const [isSearchRestaurantModal, onToggleSearchRestaurantModal] = useToggle(true); + const { data: bookmarkCheck } = bookMarkCheckQuery; + + const isBookmark = bookmarkCheck?.bookmarked || false; + + if (isLoading) return
로딩중
; + if (isError) return
에러
; + if (!data) return
; + const { detail, title, titleA, titleB, region, imageA, imageB } = data; return ( @@ -39,17 +58,24 @@ function Detail() { /> - {/* */} + @@ -78,6 +104,7 @@ const Container = styled.div` const PageInner = styled.div` padding: 20px; border-top-left-radius: 20px; + border-bottom: none; position: relative; margin: 0 auto; border-radius: 4px; diff --git a/apps/jurumarble/src/app/vote/[id]/services/useCommentServices.ts b/apps/jurumarble/src/app/vote/[id]/services/useCommentServices.ts new file mode 100644 index 00000000..d19ed6ec --- /dev/null +++ b/apps/jurumarble/src/app/vote/[id]/services/useCommentServices.ts @@ -0,0 +1,77 @@ +import { useInfiniteQuery, useMutation, useQueryClient } from "@tanstack/react-query"; +import { + getCommentById, + postComment, + PostCommentRequest, + postHateComment, + postLikeComment, +} from "lib/apis/comment"; +import { queryKeys, reactQueryKeys } from "lib/queryKeys"; +import React from "react"; + +export default function useCommentServices( + voteId: number, + sortBy: "ByTime" | "ByPopularity", + commentType: "votes" | "drinks", + paging?: { + page: number; + size: number; + }, +) { + const queryClient = useQueryClient(); + const { + data: comments, + isLoading, + isError, + fetchNextPage, + } = useInfiniteQuery( + reactQueryKeys.detailCommentList( + voteId, + commentType, + paging?.page ?? 0, + paging?.size ?? 10, + sortBy, + ), + ({ pageParam = 0 }) => + getCommentById({ + commentType, + paging: { page: pageParam, size: 100 }, + sortBy: sortBy, + typeId: voteId, + }), + { + getNextPageParam: (lastPage, pages) => { + // @NOTE 백엔드에서 last 작동이 안되어 주석 + if (lastPage.last) return undefined; + return pages.length + 1; + }, + keepPreviousData: true, + cacheTime: 1000 * 60 * 5, + staleTime: 1000 * 60 * 5, + }, + ); + + const { mutate: mutateLike } = useMutation( + (commentId: number) => postLikeComment(commentType, voteId, commentId), + { + onSuccess: () => { + queryClient.invalidateQueries([queryKeys.DETAIL_COMMENT_LIST]); + }, + }, + ); + + const { mutate: mutateHate } = useMutation( + (commentId: number) => postHateComment(commentType, voteId, commentId), + { + onSuccess: () => { + queryClient.invalidateQueries([queryKeys.DETAIL_COMMENT_LIST]); + }, + }, + ); + + const { mutate: mutateComment } = useMutation((body: PostCommentRequest) => + postComment(commentType, voteId, body), + ); + + return { comments, isLoading, isError, fetchNextPage, mutateLike, mutateHate, mutateComment }; +} diff --git a/apps/jurumarble/src/app/vote/[id]/services/useExecuteVoteService.ts b/apps/jurumarble/src/app/vote/[id]/services/useExecuteVoteService.ts new file mode 100644 index 00000000..0cdb3a06 --- /dev/null +++ b/apps/jurumarble/src/app/vote/[id]/services/useExecuteVoteService.ts @@ -0,0 +1,37 @@ +import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; + +import { AorB, getVotingCheck, postExecuteVote } from "lib/apis/vote"; + +import { queryKeys, reactQueryKeys } from "lib/queryKeys"; +import { useState } from "react"; + +export default function useExecuteVoteService(voteId: number) { + const [select, setSelect] = useState<{ choice: AorB | null }>({ choice: null }); + const queryClient = useQueryClient(); + const { mutate } = useMutation((choice: "A" | "B") => postExecuteVote(voteId, { choice }), { + onSuccess: () => { + queryClient.invalidateQueries([queryKeys.VOTE_DETAIL]); + queryClient.invalidateQueries([queryKeys.VOTING_CHECK]); + }, + onError: () => { + alert("로그인 후 진행해주세요."); + }, + }); + + const { data } = useQuery(reactQueryKeys.votingCheck(voteId), () => getVotingCheck(voteId), { + onSuccess: (data) => { + if (data.voted) { + setSelect({ choice: data.userChoice }); + } else setSelect({ choice: null }); + }, + onError: () => { + setSelect({ choice: null }); + }, + enabled: !!voteId, + // @note 캐시를 사용하지 않는다. + cacheTime: 0, + staleTime: 0, + }); + + return { mutate, select, data }; +} diff --git a/apps/jurumarble/src/app/vote/[id]/services/useVoteLoadService.ts b/apps/jurumarble/src/app/vote/[id]/services/useVoteLoadService.ts new file mode 100644 index 00000000..973c93ff --- /dev/null +++ b/apps/jurumarble/src/app/vote/[id]/services/useVoteLoadService.ts @@ -0,0 +1,16 @@ +import { useQuery } from "@tanstack/react-query"; +import { getVoteByVoteIdAPI } from "lib/apis/vote"; + +import { reactQueryKeys } from "lib/queryKeys"; + +export default function useVoteLoadService(voteId: number) { + const { data, isLoading, isError } = useQuery( + reactQueryKeys.voteDetail(voteId), + () => getVoteByVoteIdAPI(voteId), + { + enabled: !!voteId, + }, + ); + + return { data, isLoading, isError }; +} diff --git a/apps/jurumarble/src/app/vote/page.tsx b/apps/jurumarble/src/app/vote/page.tsx index 2486e0e8..a52d2135 100644 --- a/apps/jurumarble/src/app/vote/page.tsx +++ b/apps/jurumarble/src/app/vote/page.tsx @@ -4,26 +4,25 @@ import BottomBar from "components/BottomBar"; import { Button } from "components/button"; import Header from "components/Header"; import { media } from "lib/styles"; -import { useRouter } from "next/navigation"; +import { useParams, useRouter } from "next/navigation"; import { EmptyAImg, ExImg1 } from "public/images"; import React, { useState } from "react"; import SvgIcDetail from "src/assets/icons/components/IcDetail"; import styled, { css } from "styled-components"; import useFlipAnimation from "./hooks/useFlipAnimation"; import useInfiniteMainListService from "./post/services/useGetVoteListService"; +import usePostBookmarkService from "./post/services/useBookmarkService"; import ChipContainer from "./[id]/components/ChipContainer"; import VoteDescription from "./[id]/components/VoteDescription"; import Path from "lib/Path"; -import useBookmarkService from "services/useBookmarkService"; +import useExecuteVoteService from "./[id]/services/useExecuteVoteService"; export type Drag = "up" | "down" | null; function VoteHomePage() { + const params = useParams(); + const router = useRouter(); - const [selected, setSelected] = useState<"A" | "B" | null>(null); - const onMutateVoting = (select: "A" | "B") => { - setSelected(select); - }; const { isError, isLoading, mainVoteList, nowShowing, onChangeNowShowing } = useInfiniteMainListService({ @@ -37,7 +36,12 @@ function VoteHomePage() { const { title, imageA, imageB, titleA, titleB, detail, voteId, region } = mainVoteList[nowShowing] || {}; - const { mutateBookMark, bookMarkCheckQuery } = useBookmarkService(voteId); + const { mutateBookMark, bookMarkCheckQuery } = usePostBookmarkService(voteId); + + const { mutate, select } = useExecuteVoteService(voteId); + const onMutateVoting = (select: "A" | "B") => { + mutate(select); + }; const { data: bookmarkCheck } = bookMarkCheckQuery; @@ -90,7 +94,7 @@ function VoteHomePage() { titleB={titleB} totalCountA={100} totalCountB={100} - select={selected} + select={select.choice} onMutateVoting={onMutateVoting} /> router.push(`vote/${voteId}`)}> diff --git a/apps/jurumarble/src/app/vote/post/services/useBookmarkService.ts b/apps/jurumarble/src/app/vote/post/services/useBookmarkService.ts new file mode 100644 index 00000000..429c46d6 --- /dev/null +++ b/apps/jurumarble/src/app/vote/post/services/useBookmarkService.ts @@ -0,0 +1,33 @@ +import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; +import { getBookMarkCheckAPI, postBookmarkAPI } from "lib/apis/bookmark"; +import Path from "lib/Path"; +import { queryKeys, reactQueryKeys } from "lib/queryKeys"; +import { useRouter } from "next/navigation"; +import { toast } from "react-toastify"; + +export default function usePostBookmarkService(voteId: number) { + const queryClient = useQueryClient(); + + const bookMarkCheckQuery = useQuery( + reactQueryKeys.bookmarkCheck(), + () => getBookMarkCheckAPI(voteId), + { + enabled: !!voteId, + }, + ); + + const router = useRouter(); + const { mutate: mutateBookMark } = useMutation(() => postBookmarkAPI(voteId), { + onSuccess: () => { + queryClient.invalidateQueries([queryKeys.BOOKMARK_CHECK]); + toast("북마크에서 해제되었어요"); + }, + onError: () => { + if (confirm("로그인이 필요한 서비스입니다.")) { + router.push(Path.VOTE_HOME); + } + }, + }); + + return { mutateBookMark, bookMarkCheckQuery }; +} diff --git a/apps/jurumarble/src/lib/apis/comment.ts b/apps/jurumarble/src/lib/apis/comment.ts new file mode 100644 index 00000000..aeb3faf3 --- /dev/null +++ b/apps/jurumarble/src/lib/apis/comment.ts @@ -0,0 +1,93 @@ +import { http } from "./http/http"; +import { AorB } from "./vote"; + +export interface PagingRequest { + page: number; + size: number; +} + +export interface GetCommentRequest { + sortBy: "ByTime" | "ByPopularity"; + paging: PagingRequest; + commentType: "votes" | "drinks"; + typeId: number; +} + +interface CommentResponse { + id: number; + userId: number; + voteId: number; + drinkId: number; + nickName: string; + parentId: number; + content: string; + imageUrlstring: string; + gender: string; + age: string; + mbti: string; + createdDate: string; + likeCount: number; + hateCount: number; + choice: AorB; + restaurant: { + restaurantName: string; + restaurantImage: string; + }; +} + +export interface GetCommentResponse { + content: CommentResponse[]; + empty: boolean; + first: boolean; + last: boolean; + numberOfElements: number; + size: number; +} + +export const getCommentById = async ({ + commentType, + paging, + sortBy, + typeId, +}: GetCommentRequest) => { + const response = await http.get(`api/${commentType}/${typeId}/comments`, { + params: { + sortBy, + page: paging.page, + size: paging.size, + }, + }); + return response.data; +}; + +export const postLikeComment = async ( + commentType: "votes" | "drinks", + typeId: number, + commentId: number, +) => { + const response = await http.post(`/api/${commentType}/${typeId}/comments/${commentId}/likers`); + return response.data; +}; + +export const postHateComment = async ( + commentType: "votes" | "drinks", + typeId: number, + commentId: number, +) => { + const response = await http.post(`/api/${commentType}/${typeId}/comments/${commentId}/haters`); + return response.data; +}; + +export interface PostCommentRequest { + content: string; + parentId?: number | null; +} + +export const postComment = async ( + commentType: "votes" | "drinks", + voteId: number, + body: PostCommentRequest, +) => { + const response = await http.post(`/api/${commentType}/${voteId}/comments/create`, body); + return response.data; +}; diff --git a/apps/jurumarble/src/lib/apis/vote.ts b/apps/jurumarble/src/lib/apis/vote.ts index 41eb57dc..6fd5bde9 100644 --- a/apps/jurumarble/src/lib/apis/vote.ts +++ b/apps/jurumarble/src/lib/apis/vote.ts @@ -1,6 +1,8 @@ +import axios from "axios"; import { SERVER_URL } from "lib/constants"; import { SortType } from "src/types/common"; import { baseApi } from "./http/base"; +import { http } from "./http/http"; type VoteListSortType = Omit; @@ -48,29 +50,26 @@ export const getVoteListAPI = async ({ page, size, sortBy, keyword }: GetVoteLis return response.data; }; -interface Writer { - userImage: string | null; - userGender: string; - userAge: number; - userMbti: string; - nickName: string; -} - export interface GetVoteByIdResponse { - writer: Writer; - voteCreatedDate: Date; + voteId: number; + postedUserId: number; title: string; + detail: string; + filteredGender: string; + filteredAge: string; + filteredMbti: string; + votedCount: number; + voteType: string; imageA: string; imageB: string; titleA: string; titleB: string; - description: string; + region: string; } export const getVoteByVoteIdAPI = async (voteId: number) => { - const response = await fetch(`${SERVER_URL}api/votes/${voteId}`, {}); - const voteInfo = await response.json(); - return voteInfo.data; + const response = await baseApi.get(`api/votes/${voteId}`); + return response.data; }; interface ModifyVoteRequest { @@ -182,3 +181,19 @@ export const getVoteDrinkList = async (params: GetVoteDrinkListRequest) => { }); return response.data.voteSlice; }; + +export const postExecuteVote = async (voteId: number, body: { choice: "A" | "B" | null }) => { + const response = await http.post(`api/votes/${voteId}/vote`, body); + return response.data; +}; + +export type AorB = "A" | "B"; +interface GetVotingCheckResponse { + userChoice: AorB | null; + voted: boolean; +} + +export const getVotingCheck = async (voteId: number) => { + const response = await http.get(`api/votes/${voteId}/voted`); + return response.data; +}; diff --git a/apps/jurumarble/src/lib/queryKeys.ts b/apps/jurumarble/src/lib/queryKeys.ts index 34af1717..ae3f280b 100644 --- a/apps/jurumarble/src/lib/queryKeys.ts +++ b/apps/jurumarble/src/lib/queryKeys.ts @@ -6,6 +6,10 @@ export const queryKeys = { RESTAURANT_LIST: "restaurantList" as const, SEARCH_DRINK_LIST: "searchDrinkList" as const, SEARCH_VOTE_DRINK_LIST: "searchVoteDrinkList" as const, + VOTE_DETAIL: "voteDetail" as const, + VOTING_CHECK: "votingCheck" as const, + DETAIL_COMMENT_LIST: "commentByVoteId" as const, + DETAIL_VOTE_COUNT: "voteCountByVoteId" as const, }; export const reactQueryKeys = { @@ -15,4 +19,14 @@ export const reactQueryKeys = { userInfo: () => [queryKeys.USER_INFO], voteList: (params: any) => [queryKeys.VOTE_LIST, ...params], restaurantList: (params: any) => [queryKeys.RESTAURANT_LIST, ...params], + voteDetail: (voteId: number) => [queryKeys.VOTE_DETAIL, voteId] as const, + votingCheck: (id: number) => [queryKeys.VOTING_CHECK, id] as const, + detailCommentList: ( + typeId: number, + commentType: "votes" | "drinks", + size?: number, + page?: number, + sortBy?: string, + ) => [queryKeys.DETAIL_COMMENT_LIST, typeId, commentType, size, page, sortBy] as const, + detailVoteCount: (id: number) => [queryKeys.DETAIL_VOTE_COUNT, id] as const, };