diff --git a/apps/jurumarble/next.config.js b/apps/jurumarble/next.config.js index bbd85efd..02d7087d 100644 --- a/apps/jurumarble/next.config.js +++ b/apps/jurumarble/next.config.js @@ -9,15 +9,18 @@ const nextConfig = { domains: [ "shopping-phinf.pstatic.net", "elasticbeanstalk-ap-northeast-2-319210348301.s3.ap-northeast-2.amazonaws.com", + "tong.visitkorea.or.kr", "img.danawa.com", "sulsulsul.com", "shop-phinf.pstatic.net", - "www.sulseam.com", - "mblogthumb-phinf.pstatic.net", - "sogoodk.com", "modo-phinf.pstatic.net", - "cdn-pro-web-251-115.cdn-nhncommerce.com", + "cdn-pro-web", "lh3.googleusercontent.com", + "cdn-std-web", + "cdn.imweb.me", + "www.xn--2q1bq25atga3iu9cz95a.com", + "www.seenews365.com", + "www.seongpo.co.kr", ], }, }; diff --git a/apps/jurumarble/src/app/drink-info/[id]/components/DrinkCommentContainer.tsx b/apps/jurumarble/src/app/drink-info/[id]/components/DrinkCommentContainer.tsx index d64d52fb..0085d059 100644 --- a/apps/jurumarble/src/app/drink-info/[id]/components/DrinkCommentContainer.tsx +++ b/apps/jurumarble/src/app/drink-info/[id]/components/DrinkCommentContainer.tsx @@ -4,7 +4,6 @@ import CommentForm from "app/vote/[id]/components/CommentForm"; import CommentToolBar from "app/vote/[id]/components/CommentToolbar"; import useCommentServices from "app/vote/[id]/services/useCommentServices"; import { queryKeys } from "lib/queryKeys"; -import Link from "next/link"; import { useParams } from "next/navigation"; import { useState } from "react"; import styled from "styled-components"; @@ -70,11 +69,11 @@ function DrinkCommentContainer() { createdDate, gender, hateCount, - imageUrlstring, likeCount, mbti, nickName, userId, + restaurant, }, index, ) => ( @@ -88,11 +87,11 @@ function DrinkCommentContainer() { createdDate, gender, hateCount, - imageUrlstring, likeCount, mbti, nickName, userId: userId, + restaurant, }} mutateLike={() => mutateLike(id)} mutateHate={() => mutateHate(id)} diff --git a/apps/jurumarble/src/app/layout.tsx b/apps/jurumarble/src/app/layout.tsx index 3fe7bf4c..65c6a297 100644 --- a/apps/jurumarble/src/app/layout.tsx +++ b/apps/jurumarble/src/app/layout.tsx @@ -6,6 +6,7 @@ import type { Metadata } from "next"; import { ToastContainer } from "react-toastify"; import "react-toastify/dist/ReactToastify.css"; import { injectStyle } from "react-toastify/dist/inject-style"; +import AuthProcess from "components/AuthProcess"; export const metadata: Metadata = { title: "주루마블", @@ -24,11 +25,10 @@ export default function RootLayout({ children }: { children: React.ReactNode }) - <> - - {children} - - > + + + {children} + diff --git a/apps/jurumarble/src/app/my/components/UseInfoContainer.tsx b/apps/jurumarble/src/app/my/components/UseInfoContainer.tsx index 19c923ed..287e75f2 100644 --- a/apps/jurumarble/src/app/my/components/UseInfoContainer.tsx +++ b/apps/jurumarble/src/app/my/components/UseInfoContainer.tsx @@ -6,7 +6,6 @@ import styled, { css } from "styled-components"; import Image from "next/image"; import { DrinkImage } from "public/images"; import useGetUserInfo from "services/useGetUserInfo"; -import { useRouter } from "next/navigation"; function UserInfoContainer() { const { userInfo } = useGetUserInfo(); @@ -15,9 +14,6 @@ function UserInfoContainer() { const { gender, nickname, yearOfBirth, mbti, imageUrl } = userInfo; - const router = useRouter(); - if (!(gender && yearOfBirth && mbti)) router.replace(Path.REGISTER_PAGE); - const date = new Date(); const age = date.getFullYear() - yearOfBirth; const ageRange = Math.floor(age / 10) * 10; diff --git a/apps/jurumarble/src/app/my/components/VoteItem.tsx b/apps/jurumarble/src/app/my/components/VoteItem.tsx index db9130c0..febc59d7 100644 --- a/apps/jurumarble/src/app/my/components/VoteItem.tsx +++ b/apps/jurumarble/src/app/my/components/VoteItem.tsx @@ -1,6 +1,5 @@ import Path from "lib/Path"; import { useRouter } from "next/navigation"; -import { ExImg1 } from "public/images"; import useBookmarkService from "services/useBookmarkService"; import { Content } from "src/types/vote"; import styled, { css } from "styled-components"; @@ -8,7 +7,6 @@ import ChipContainer from "./ChipContainer"; import VoteDescription from "./VoteDescription"; type Props = Pick; -const getSafeImage = (image: string) => (image.includes("http") ? image : ExImg1); function VoteItem({ voteId, region, title, imageA, imageB }: Props) { const { isBookmark, mutateBookMark } = useBookmarkService(voteId); @@ -27,7 +25,7 @@ function VoteItem({ voteId, region, title, imageA, imageB }: Props) { mutateBookMark={mutateBookMark} isBookmark={isBookmark} /> - + ); } diff --git a/apps/jurumarble/src/app/search/components/DrinkVoteList.tsx b/apps/jurumarble/src/app/search/components/DrinkVoteList.tsx index ac2f4ac3..1aa7a688 100644 --- a/apps/jurumarble/src/app/search/components/DrinkVoteList.tsx +++ b/apps/jurumarble/src/app/search/components/DrinkVoteList.tsx @@ -27,8 +27,6 @@ function DrinkVoteList({ searchText, sortOption, regionOption }: Props) { return <>>; } - console.log(voteDrinkList); - return ( {voteDrinkList.map((voteDrink, index) => ( diff --git a/apps/jurumarble/src/app/vote/[id]/components/Comment.tsx b/apps/jurumarble/src/app/vote/[id]/components/Comment.tsx index 113aba02..6c2f7480 100644 --- a/apps/jurumarble/src/app/vote/[id]/components/Comment.tsx +++ b/apps/jurumarble/src/app/vote/[id]/components/Comment.tsx @@ -2,20 +2,20 @@ import { useOutsideClick, useToggle } from "@monorepo/hooks"; import ModifyDeleteButtonBox from "app/vote/components/MenuBox"; import NonWriterBox from "app/vote/components/NonWriterBox"; import { Button } from "components/button"; -import { CommentResponse } from "lib/apis/comment"; import Image from "next/image"; import { ExImg1 } from "public/images"; import React, { useEffect, useState } from "react"; import { toast } from "react-toastify"; import useGetUserInfo from "services/useGetUserInfo"; +import { SvgIcMapPin } from "src/assets/icons/components"; import SvgIcMenu from "src/assets/icons/components/IcMenu"; import styled, { css } from "styled-components"; import useCommentDeleteService from "../services/useCommentDeleteService"; import useCommentReportService from "../services/useCommentReportService"; - import CommentDeleteModal from "./CommentDeleteModal"; import CommentForm from "./CommentForm"; import CommentPutForm from "./CommentPutForm"; +import SearchRestaurantModal from "./SearchRestaurantModal"; interface Props { voteType: "drinks" | "votes"; @@ -27,11 +27,14 @@ interface Props { createdDate: string; gender: string; hateCount: number; - imageUrlstring: string; likeCount: number; mbti: string; nickName: string; userId: number; + restaurant: { + restaurantName: string; + restaurantImage: string; + }; }; mutateLike?(): void; mutateHate?(): void; @@ -47,17 +50,17 @@ function Comment({ comment, mutateLike, mutateHate, voteType, postId }: Props) { createdDate, gender, hateCount, - // imageUrl, likeCount, mbti, nickName, userId, + restaurant, } = comment; const [toggleMenu, onToggleMenu] = useToggle(false); const [toggleNonWriterMenu, onToggleNonWriterMenu] = useToggle(false); - const { targetEl } = useOutsideClick(toggleMenu, onToggleMenu); - const { targetEl: targetEl2 } = useOutsideClick( + const { targetEl } = useOutsideClick(toggleMenu, onToggleMenu); + const { targetEl: targetEl2 } = useOutsideClick( toggleNonWriterMenu, onToggleNonWriterMenu, ); @@ -76,6 +79,7 @@ function Comment({ comment, mutateLike, mutateHate, voteType, postId }: Props) { useEffect(() => { setCommentForm(content); }, [comment]); + const [isSearchRestaurantModal, onToggleSearchRestaurantModal] = useToggle(); return ( @@ -91,7 +95,7 @@ function Comment({ comment, mutateLike, mutateHate, voteType, postId }: Props) { /> {(age || gender || mbti) && ( - + {gender && gender} {age && ( @@ -105,7 +109,16 @@ function Comment({ comment, mutateLike, mutateHate, voteType, postId }: Props) { > )} - + {userInfo?.userId === userId ? ( + + + + ) : ( + + + + )} + )} {nickName} @@ -134,13 +147,34 @@ function Comment({ comment, mutateLike, mutateHate, voteType, postId }: Props) { ) : ( <> {content} - {userId === userInfo?.userId && ( + {restaurant ? ( + <> + + + + + + {restaurant.restaurantName} + + > + ) : userInfo?.userId === userId ? ( - + 음식점 추가 ﹢ - )} + ) : null} {createdDate.slice(0, 10)}・ ❤️ 좋아요 {likeCount ?? 0}{" "} @@ -150,15 +184,6 @@ function Comment({ comment, mutateLike, mutateHate, voteType, postId }: Props) { > )} - {userId === userInfo?.userId ? ( - - - - ) : ( - - - - )} {toggleMenu && ( )} + {isSearchRestaurantModal && ( + + )} ); } @@ -191,9 +223,10 @@ const Container = styled.div` position: relative; `; -const Flex = styled.div` +const FlexBetween = styled.div` display: flex; align-items: center; + justify-content: space-between; gap: 6px; `; @@ -212,6 +245,26 @@ const Contents = styled.div` `} `; +const RestaurantImage = styled.div` + width: 100%; + aspect-ratio: 16 / 9; + position: relative; +`; + +const RestaurantNameBox = styled.div` + ${({ theme }) => css` + ${theme.typography.caption_chip} + color: ${theme.colors.black_04}; + background-color: ${theme.colors.bg_01}; + padding: 4px 8px; + border-radius: 4px; + gap: 4px; + display: flex; + align-items: center; + margin-top: 8px; + `} +`; + const TagBox = styled.div` display: flex; align-items: center; @@ -248,6 +301,7 @@ const CommentInfo = styled.div` display: flex; align-items: center; gap: 4px; + margin-top: 12px; ${({ theme }) => css` color: ${theme.colors.black_04}; ${theme.typography.caption_chip} diff --git a/apps/jurumarble/src/app/vote/[id]/components/CommentContainer.tsx b/apps/jurumarble/src/app/vote/[id]/components/CommentContainer.tsx index 1e3c8a04..394e0b03 100644 --- a/apps/jurumarble/src/app/vote/[id]/components/CommentContainer.tsx +++ b/apps/jurumarble/src/app/vote/[id]/components/CommentContainer.tsx @@ -74,11 +74,11 @@ function CommentContainer({ postId }: Props) { createdDate, gender, hateCount, - imageUrlstring, likeCount, mbti, nickName, userId, + restaurant, }, index, ) => ( @@ -92,11 +92,11 @@ function CommentContainer({ postId }: Props) { createdDate, gender, hateCount, - imageUrlstring, likeCount, mbti, nickName, userId: userId, + restaurant, }} mutateLike={() => mutateLike(id)} mutateHate={() => mutateHate(id)} diff --git a/apps/jurumarble/src/app/vote/[id]/components/RestaurantItem.tsx b/apps/jurumarble/src/app/vote/[id]/components/RestaurantItem.tsx index 07526abb..16205afb 100644 --- a/apps/jurumarble/src/app/vote/[id]/components/RestaurantItem.tsx +++ b/apps/jurumarble/src/app/vote/[id]/components/RestaurantItem.tsx @@ -1,16 +1,22 @@ +import { RestaurantInfo } from "lib/apis/restaurant"; import Image from "next/image"; -import { EmptyAImg } from "public/images"; import styled, { css } from "styled-components"; -function RestaurantItem() { +interface Props { + onClickSelectedRestaurant: (restaurantId: RestaurantInfo) => void; + restaurantInfo: RestaurantInfo; +} + +function RestaurantItem({ restaurantInfo, onClickSelectedRestaurant }: Props) { + const { restaurantName, treatMenu, restaurantImage } = restaurantInfo; return ( - - + onClickSelectedRestaurant(restaurantInfo)}> + - 이름 + {restaurantName} <>대표메뉴 : > - ㅇㄴㅁㅇㄴㅁ + {treatMenu} @@ -20,6 +26,7 @@ function RestaurantItem() { const Container = styled.li` display: flex; margin: 20px 0; + cursor: pointer; `; const TextContainer = styled.div` diff --git a/apps/jurumarble/src/app/vote/[id]/components/SearchInput.tsx b/apps/jurumarble/src/app/vote/[id]/components/SearchInput.tsx new file mode 100644 index 00000000..021ffb0b --- /dev/null +++ b/apps/jurumarble/src/app/vote/[id]/components/SearchInput.tsx @@ -0,0 +1,88 @@ +"use client"; + +import { Button } from "components/button"; +import { Input } from "components/input"; +import { FormEvent, forwardRef, MouseEvent } from "react"; +import { SvgIcX } from "src/assets/icons/components"; +import SvgIcSearch from "src/assets/icons/components/IcSearch"; +import styled, { css, useTheme } from "styled-components"; + +interface Props extends Omit, "width"> { + placeholder?: string; + eventHandler?: (e: FormEvent | MouseEvent) => void; + onChangeSearchText: (keyword: string) => void; +} + +const SearchInput = forwardRef( + ({ value, onChangeSearchText, placeholder, eventHandler }, ref) => { + const theme = useTheme(); + return ( + { + e.preventDefault(); + eventHandler?.(e); + }} + > + { + onChangeSearchText(e.target.value); + }} + > + + + {/* { + if (ref) { + ref.value = ""; + } + }} + /> */} + + + ); + }, +); + +const Search = styled.form` + display: flex; + margin-top: 8px; + width: 100%; +`; + +const InputStyled = styled(Input)` + ${({ theme }) => css` + ${theme.typography.body02} + background-color: ${theme.colors.bg_02}; + height: 44px; + padding: 10px 12px; + border-radius: 8px 0 0 8px; + ::placeholder { + color: ${theme.colors.black_05}; + } + `} +`; + +const SearchButton = styled(Button)` + ${({ theme }) => css` + background-color: ${theme.colors.bg_02}; + display: flex; + justify-content: center; + align-items: center; + border-radius: 0 8px 8px 0; + width: 44px; + height: 44px; + padding-right: 10px; + `} +`; + +export default SearchInput; diff --git a/apps/jurumarble/src/app/vote/[id]/components/SearchRestaurantModal.tsx b/apps/jurumarble/src/app/vote/[id]/components/SearchRestaurantModal.tsx index 3aaa84f7..8d4f623c 100644 --- a/apps/jurumarble/src/app/vote/[id]/components/SearchRestaurantModal.tsx +++ b/apps/jurumarble/src/app/vote/[id]/components/SearchRestaurantModal.tsx @@ -1,33 +1,63 @@ "use client"; import { Button, ModalTemplate } from "components/index"; + import VoteHeader from "components/VoteHeader"; +import useInput from "hooks/useInput"; +import { RestaurantInfo } from "lib/apis/restaurant"; import { transitions } from "lib/styles"; import Image from "next/image"; -import { EmptyAImg } from "public/images"; import { useState } from "react"; import SvgIcX from "src/assets/icons/components/IcX"; import styled, { css, DefaultTheme } from "styled-components"; +import useRestaurantImageService from "../services/useRestaurantImageService"; +import useRestaurantService from "../services/useRestaurantService"; import RestaurantItem from "./RestaurantItem"; +import SearchInput from "./SearchInput"; interface Props { + commentId: number; + postId: number; onToggleSearchRestaurantModal: () => void; } -const TEMP_LIST = [ - { manufacturer: "gyeonggiDo", drinkName: "경기도" }, - { manufacturer: "chungcheongDo", drinkName: "충청도" }, - { manufacturer: "gyeongsangDo", drinkName: "경상도" }, - { manufacturer: "ulsan", drinkName: "울산" }, - { manufacturer: "jeju", drinkName: "제주" }, -]; - -function SearchRestaurantModal({ onToggleSearchRestaurantModal }: Props) { - const [selected, setSelected] = useState(""); - const onClickSelected = (e: React.MouseEvent) => { - setSelected(e.currentTarget.name); - e.currentTarget.name === "nonSelect" && setSelected("nonSelect"); +function SearchRestaurantModal({ commentId, postId, onToggleSearchRestaurantModal }: Props) { + const [selectedRestaurant, setSelectedRestaurant] = useState(null); + const onClickSelectedRestaurant = (restaurantInfo: RestaurantInfo) => { + setSelectedRestaurant((prev) => + restaurantInfo.contentId === prev?.contentId ? null : restaurantInfo, + ); }; + + const { debouncedValue: searchText, onChange: onChangeSearchText } = useInput({ + initialValue: "", + useDebounce: true, + debounceTimeout: 500, + }); + + const { restaurantList, subscribe } = useRestaurantService({ + commentType: "votes", + typeId: postId, + commentId, + keyword: searchText, + page: 1, + region: "ALL", + }); + + if (!restaurantList) return null; + + const [selectedImage, setSelectedImage] = useState(""); + const onClickSelectedImage = (imageUrl: string) => { + selectedImage === imageUrl ? setSelectedImage("") : setSelectedImage(imageUrl); + }; + + const { restaurantImageList, postRestaurantImage } = useRestaurantImageService({ + commentType: "votes", + typeId: postId, + commentId, + contentId: selectedRestaurant?.contentId!, + }); + return ( } > - 이미지 선택 + {selectedRestaurant ? "음식점 검색" : "이미지 선택"} - - - - - 선택 안함 - - {TEMP_LIST.map(({ manufacturer }) => ( - - - - - - - ))} - - - 완료 - + {selectedRestaurant ? ( + <> + + + onClickSelectedImage("nonSelect")} + > + + 선택 안함 + + {restaurantImageList && + restaurantImageList.map((restaurantImage) => ( + + onClickSelectedImage(restaurantImage)} + > + + + + + ))} + + { + postRestaurantImage({ + commentType: "votes", + typeId: postId, + commentId, + restaurantName: selectedRestaurant.restaurantName, + restaurantImage: selectedImage, + }); + onToggleSearchRestaurantModal(); + }} + > + 완료 + + > + ) : ( + <> + + + {restaurantList.map((restaurantInfo) => ( + <> + {restaurantInfo && ( + + )} + > + ))} + + + > + )} - {/** - * @Note 음식점 검색 - */} - {/* - - - } - > - 음식점 검색 - - - - - {TEMP_LIST.map(() => ( - - ))} - - */} ); } @@ -115,6 +166,16 @@ const CloseButton = styled(Button)` margin: 14px 12px 0 0; `; +const RestaurantList = styled.ul` + ${({ theme }) => css` + display: flex; + flex-direction: column; + margin-top: 8px; + overflow: auto; + height: 70vh; + `} +`; + const FoodImageList = styled.ul` display: flex; flex-wrap: wrap; @@ -123,11 +184,6 @@ const FoodImageList = styled.ul` border-top: 1px solid ${({ theme }) => theme.colors.line_01}; overflow: auto; padding-bottom: 80px; - -ms-overflow-style: none /* IE and Edge 스크롤바 없애는 css*/; - scrollbar-width: none; /* Firefox 스크롤바 없애는 css */ - &::-webkit-scrollbar { - display: none; /* Chrome , Safari , Opera 스크롤바 없애는 css*/ - } `; const NonSelectedButton = styled(Button)` @@ -138,8 +194,8 @@ const FoodItem = styled.div` position: relative; `; -const ColorBox = styled.div<{ theme: DefaultTheme; selected: boolean }>` - ${({ theme, selected }) => +const ColorBox = styled.div<{ theme: DefaultTheme; selectedImage: boolean }>` + ${({ theme, selectedImage }) => css` border-radius: 4px; position: absolute; @@ -147,7 +203,7 @@ const ColorBox = styled.div<{ theme: DefaultTheme; selected: boolean }>` left: 0; width: 100%; height: 100%; - ${selected && + ${selectedImage && css` border: 2px solid ${theme.colors.main_01}; background: rgba(255, 74, 22, 0.7); diff --git a/apps/jurumarble/src/app/vote/[id]/page.tsx b/apps/jurumarble/src/app/vote/[id]/page.tsx index 8eefc86f..d1433dfe 100644 --- a/apps/jurumarble/src/app/vote/[id]/page.tsx +++ b/apps/jurumarble/src/app/vote/[id]/page.tsx @@ -9,8 +9,6 @@ import VoteDescription from "./components/VoteDescription"; import ChipContainer from "./components/ChipContainer"; import CommentContainer from "./components/CommentContainer"; import { useParams } from "next/navigation"; -import { useToggle } from "@monorepo/hooks"; -import SearchRestaurantModal from "./components/SearchRestaurantModal"; import useVoteLoadService from "./services/useVoteLoadService"; import useExecuteVoteService from "./services/useExecuteVoteService"; import useFilteredStatisticsService from "./services/useFilterStatisticsService"; @@ -29,8 +27,6 @@ function Detail() { const postId = params.id; - const [isSearchRestaurantModal, onToggleSearchRestaurantModal] = useToggle(false); - const { data, isError, isLoading } = useVoteLoadService(Number(postId)); const { mutateBookMark, isBookmark } = useBookmarkService(Number(postId)); @@ -75,7 +71,6 @@ function Detail() { return ( - - - {isSearchRestaurantModal && ( - - )} - ); diff --git a/apps/jurumarble/src/app/vote/[id]/services/useRestaurantImageService.ts b/apps/jurumarble/src/app/vote/[id]/services/useRestaurantImageService.ts new file mode 100644 index 00000000..c5219089 --- /dev/null +++ b/apps/jurumarble/src/app/vote/[id]/services/useRestaurantImageService.ts @@ -0,0 +1,36 @@ +import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; +import { getRestaurantImageAPI, postRestaurantImageAPI } from "lib/apis/restaurant"; +import { queryKeys } from "lib/queryKeys"; + +type GetRestaurantImageListParams = Exclude[0], undefined>; +type PostRestaurantImageParams = Exclude[0], undefined>; + +const getRestaurantImageListQueryKey = ( + getRestaurantImageListParams: GetRestaurantImageListParams, +) => [queryKeys.RESTAURANT_IMAGE_LIST, getRestaurantImageListParams]; + +export default function useRestaurantImageService( + getRestaurantImageListParams: GetRestaurantImageListParams, +) { + const { data: restaurantImageList } = useQuery( + getRestaurantImageListQueryKey(getRestaurantImageListParams), + () => getRestaurantImageAPI(getRestaurantImageListParams), + { + enabled: !!getRestaurantImageListParams.contentId, + }, + ); + + const queryClient = useQueryClient(); + + const { mutate: postRestaurantImage } = useMutation( + (postRestaurantImageParams: PostRestaurantImageParams) => + postRestaurantImageAPI(postRestaurantImageParams), + { + onSuccess: () => { + queryClient.invalidateQueries([queryKeys.DETAIL_COMMENT_LIST]); + }, + }, + ); + + return { restaurantImageList, postRestaurantImage }; +} diff --git a/apps/jurumarble/src/app/vote/[id]/services/useRestaurantService.ts b/apps/jurumarble/src/app/vote/[id]/services/useRestaurantService.ts new file mode 100644 index 00000000..b3c17f73 --- /dev/null +++ b/apps/jurumarble/src/app/vote/[id]/services/useRestaurantService.ts @@ -0,0 +1,35 @@ +import { useInfiniteQuery } from "@tanstack/react-query"; +import { getRestaurantAPI } from "lib/apis/restaurant"; +import { queryKeys } from "lib/queryKeys"; +import { useInfiniteScroll } from "@monorepo/hooks"; + +type GetRestaurantListParams = Exclude[0], undefined>; + +const getRestaurantListQueryKey = (params: GetRestaurantListParams) => [ + queryKeys.RESTAURANT_LIST, + params, +]; + +export default function useRestaurantService(params: GetRestaurantListParams) { + const { data, fetchNextPage } = useInfiniteQuery( + getRestaurantListQueryKey(params), + ({ pageParam }) => { + return getRestaurantAPI({ ...params, page: pageParam?.page || 1 }); + }, + { + getNextPageParam: ({ last, number }) => { + if (last) return undefined; + return { + page: number + 2, + }; + }, + keepPreviousData: true, + }, + ); + + const [subscribe] = useInfiniteScroll(fetchNextPage); + + const restaurantList = data?.pages.flatMap((page) => page.content) ?? []; + + return { restaurantList, subscribe }; +} diff --git a/apps/jurumarble/src/components/AorBMark.tsx b/apps/jurumarble/src/components/AorBMark.tsx index 90d6ad6b..13722391 100644 --- a/apps/jurumarble/src/components/AorBMark.tsx +++ b/apps/jurumarble/src/components/AorBMark.tsx @@ -13,6 +13,7 @@ const AorBMarkStyled = styled.div<{ AorB: string }>` ${({ theme, AorB }) => css` ${theme.typography.caption_chip} background-color: ${AorB === "A" ? theme.colors.sub_01 : theme.colors.sub_02}; + color: ${theme.colors.white}; position: absolute; top: 0; left: 0; diff --git a/apps/jurumarble/src/components/AuthProcess.tsx b/apps/jurumarble/src/components/AuthProcess.tsx new file mode 100644 index 00000000..97323ccf --- /dev/null +++ b/apps/jurumarble/src/components/AuthProcess.tsx @@ -0,0 +1,17 @@ +"use client"; + +import { useRouter } from "next/navigation"; +import useGetUserInfo from "services/useGetUserInfo"; +import Path from "lib/Path"; + +function AuthProcess() { + const { userInfo } = useGetUserInfo(); + if (!userInfo) return null; + const { gender, yearOfBirth, mbti } = userInfo!; + const router = useRouter(); + if (!(gender && yearOfBirth && mbti)) router.replace(Path.REGISTER_PAGE); + + return <>>; +} + +export default AuthProcess; diff --git a/apps/jurumarble/src/components/textarea/Textarea.tsx b/apps/jurumarble/src/components/textarea/Textarea.tsx new file mode 100644 index 00000000..bee6aede --- /dev/null +++ b/apps/jurumarble/src/components/textarea/Textarea.tsx @@ -0,0 +1,38 @@ +import React, { InputHTMLAttributes, TextareaHTMLAttributes } from "react"; +import styled, { css } from "styled-components"; + +interface TextareaProps extends TextareaHTMLAttributes { + /** + * Input 형태 + */ + variant?: "standard" | "outlined"; + /** + * Input 가로 길이 + */ + width: `${number}px` | `${number}%` | "auto"; + /** + * Input 가로 길이 + */ + height: `${number}px` | `${number}%` | "auto"; + /** + * 포커스 여부 + * 기본 값 : false + */ + autoFocus?: boolean; +} + +function Textarea({ width, height, variant, ...rest }: TextareaProps) { + return ; +} + +const TextareaStyled = styled.textarea` + ${({ theme, width, height }) => + css` + color: ${theme.colors.black_01} + width: ${width}; + height: ${height}; + border: none; + `} +`; + +export default Textarea; diff --git a/apps/jurumarble/src/hooks/useInput.ts b/apps/jurumarble/src/hooks/useInput.ts index af176c46..878b3e4b 100644 --- a/apps/jurumarble/src/hooks/useInput.ts +++ b/apps/jurumarble/src/hooks/useInput.ts @@ -1,7 +1,7 @@ import { useCallback, useState } from "react"; import { useDebouncedCallback } from "@react-hookz/web"; -export interface UseInputHookType { +interface UseInputHookType { /** * input state가 initializing 될 때 넣을 값입니다. * diff --git a/apps/jurumarble/src/lib/apis/comment.ts b/apps/jurumarble/src/lib/apis/comment.ts index c53757f9..1e767899 100644 --- a/apps/jurumarble/src/lib/apis/comment.ts +++ b/apps/jurumarble/src/lib/apis/comment.ts @@ -22,7 +22,7 @@ export interface CommentResponse { nickName: string; parentId: number; content: string; - imageUrlstring: string; + imageUrl: string; gender: string; age: string; mbti: string; diff --git a/apps/jurumarble/src/lib/apis/restaurant.ts b/apps/jurumarble/src/lib/apis/restaurant.ts index 29d3a3f2..0c8af062 100644 --- a/apps/jurumarble/src/lib/apis/restaurant.ts +++ b/apps/jurumarble/src/lib/apis/restaurant.ts @@ -1,92 +1,157 @@ import { SERVER_URL } from "lib/constants"; import { http } from "./http/http"; +type CommentType = "votes" | "drinks"; + interface GetRestaurantRequest { - voteId: number; + commentType: CommentType; + typeId: number; commentId: number; keyword: string; - areaCode?: number; + region: string; page: number; } -interface GetRestaurantResponse { +export interface RestaurantInfo { contentId: string; restaurantName: string; restaurantImage: string; treatMenu: string; } +interface Sort { + sorted: boolean; + unsorted: boolean; + empty: boolean; +} + +interface Pageable { + sort: Sort; + pageNumber: number; + pageSize: number; + offset: number; + paged: boolean; + unpaged: boolean; +} + +export interface GetRestaurantResponse { + content: RestaurantInfo[]; + pageable: Pageable; + sort: Sort; + first: boolean; + last: boolean; + totalPages: number; + totalElements: number; + size: number; + number: number; + empty: boolean; +} + export const getRestaurantAPI = async ({ - voteId, + commentType, + typeId, commentId, keyword, - areaCode, + region, page, }: GetRestaurantRequest) => { - return await http.get( - `${SERVER_URL}api/votes/${voteId}/comments/${commentId}/restaurant`, + const response = await http.get( + `api/${commentType}/${typeId}/comments/${commentId}/restaurant`, { params: { keyword, - areaCode, + region, page, }, }, ); + return response.data; }; +// export const getRestaurantAPI = async ({ +// typeId, +// commentId, +// keyword, +// region, +// page, +// }: GetRestaurantRequest) => { +// const response = await fetch( +// `${SERVER_URL}api/votes/${typeId}/comments/${commentId}/restaurant?keyword=${keyword}®ion=${region}&page=${page}`, +// ); +// const res = await response.json(); +// return res.data; +// }; + interface PutRestaurantRequest { - voteId: number; + typeId: number; commentId: number; restaurantName: string; restaurantImage: string; } export const putRestaurantAPI = async ({ - voteId, + typeId, commentId, restaurantName, restaurantImage, }: PutRestaurantRequest) => { - return await http.put(`${SERVER_URL}api/votes/${voteId}/comments/${commentId}/restaurant`, { + return await http.put(`${SERVER_URL}api/votes/${typeId}/comments/${commentId}/restaurant`, { restaurantName, restaurantImage, }); }; -interface PutRestaurantRequest { - voteId: number; +interface GetRestaurantImageRequest { + commentType: CommentType; + typeId: number; commentId: number; contentId: string; } +type GetRestaurantImageResponse = string[]; + export const getRestaurantImageAPI = async ({ - voteId, + commentType, + typeId, commentId, contentId, -}: PutRestaurantRequest) => { - return await http.put( - `${SERVER_URL}api/votes/${voteId}/comments/${commentId}/restaurant/${contentId}`, +}: GetRestaurantImageRequest) => { + const response = await http.get( + `api/${commentType}/${typeId}/comments/${commentId}/restaurant/${contentId}`, ); + return response.data; }; - -// export const getRestaurantAPI = async ({ -// voteId, +// export const getRestaurantImageAPI = async ({ +// typeId, // commentId, -// keyword, -// areaCode, -// page, -// }: GetRestaurantRequest) => { -// const response = await fetch( -// `${SERVER_URL}api/votes/${voteId}/comments/${commentId}/restaurant`, -// { -// body: JSON.stringify({ -// keyword, -// areaCode, -// page, -// }), -// }, +// contentId, +// }: GetRestaurantImageRequest) => { +// return await http.put( +// `${SERVER_URL}api/votes/${typeId}/comments/${commentId}/restaurant/${contentId}`, // ); -// const res = await response.json(); -// return res.data; // }; + +interface PostRestaurantImageRequest { + restaurantName: string; + restaurantImage: string; + commentType: CommentType; + typeId: number; + commentId: number; +} + +export const postRestaurantImageAPI = async ({ + restaurantName, + restaurantImage, + commentType, + typeId, + commentId, +}: PostRestaurantImageRequest) => { + const response = await http.post( + `api/${commentType}/${typeId}/comments/${commentId}/restaurant`, + { + restaurantName, + restaurantImage, + }, + ); + return response.data; +}; diff --git a/apps/jurumarble/src/lib/apis/vote.ts b/apps/jurumarble/src/lib/apis/vote.ts index 0b294c39..1717f625 100644 --- a/apps/jurumarble/src/lib/apis/vote.ts +++ b/apps/jurumarble/src/lib/apis/vote.ts @@ -66,6 +66,12 @@ export const getVoteByVoteIdAPI = async (voteId: number) => { return response.data; }; +// export const getVoteByVoteIdAPI = async (voteId: number) => { +// const response = await fetch(`${SERVER_URL}api/votes/${voteId}`, {}); +// const voteInfo = await response.json(); +// return voteInfo.data; +// }; + interface ModifyVoteRequest { title: string; detail: string; diff --git a/apps/jurumarble/src/lib/queryKeys.ts b/apps/jurumarble/src/lib/queryKeys.ts index 8056ad08..c2e45a43 100644 --- a/apps/jurumarble/src/lib/queryKeys.ts +++ b/apps/jurumarble/src/lib/queryKeys.ts @@ -6,6 +6,7 @@ export const queryKeys = { USER_INFO: "userInfo" as const, VOTE_LIST: "voteList" as const, RESTAURANT_LIST: "restaurantList" as const, + RESTAURANT_IMAGE_LIST: "restaurantImageList" as const, SEARCH_DRINK_LIST: "searchDrinkList" as const, SEARCH_VOTE_DRINK_LIST: "searchVoteDrinkList" as const, VOTE_DETAIL: "voteDetail" as const, @@ -28,7 +29,6 @@ export const reactQueryKeys = { // @note any 처리 mainVoteList: () => [queryKeys.MAIN_VOTE_LIST] as const, 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: ( diff --git a/apps/jurumarble/src/services/useGetUserInfo.ts b/apps/jurumarble/src/services/useGetUserInfo.ts index 6c194fef..42c7dc87 100644 --- a/apps/jurumarble/src/services/useGetUserInfo.ts +++ b/apps/jurumarble/src/services/useGetUserInfo.ts @@ -8,11 +8,11 @@ export default function useGetUserInfo() { const { data: userInfo } = useQuery(getQueryKey, getUserInfo, { placeholderData: () => ({ gender: "MALE", - nickname: "주루마블", + nickname: "", yearOfBirth: 1990, imageUrl: "", mbti: "ESTJ", - alcoholLimit: "LOW", + alcoholLimit: "", email: "", userId: 0, }),