From 55d0c587666119925190a1f3f13bdda064df617b Mon Sep 17 00:00:00 2001 From: halion Date: Fri, 14 Mar 2025 09:36:29 +0900 Subject: [PATCH 1/2] =?UTF-8?q?[#182]=20feat:=20=EB=8F=99=EC=95=84?= =?UTF-8?q?=EB=A6=AC=20=EB=94=94=ED=85=8C=EC=9D=BC=20=ED=8E=98=EC=9D=B4?= =?UTF-8?q?=EC=A7=80=20api=20=EC=A0=81=EC=9A=A9=20(#213)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/types/club.ts | 3 +- src/{features => domain}/Club/constants.ts | 0 .../Club/hooks/useGetClubSearch.ts | 2 +- .../Club/hooks/usePostClubLike.ts | 15 +++------- src/domain/Club/hooks/useReadClubDetail.ts | 15 ++++++++++ src/domain/Club/queries.ts | 7 +++++ .../Club/components/CategoryDrawer/index.tsx | 4 +-- .../Club/components/ClubCard/index.tsx | 14 +++++---- .../Club/components/ClubCard/style.css.ts | 4 ++- .../Club/components/ClubList/index.tsx | 8 ++--- .../DesktopCategorySelector.tsx/index.tsx | 2 +- .../MobileCategorySelector.tsx/index.tsx | 2 +- src/features/Club/queries.ts | 5 ---- src/features/ClubDetail/index.tsx | 27 ++++++++++++----- src/features/ClubDetail/style.css.ts | 30 ++++++++++++------- src/types/club.ts | 2 +- 16 files changed, 88 insertions(+), 52 deletions(-) rename src/{features => domain}/Club/constants.ts (100%) rename src/{features => domain}/Club/hooks/useGetClubSearch.ts (95%) rename src/{features => domain}/Club/hooks/usePostClubLike.ts (50%) create mode 100644 src/domain/Club/hooks/useReadClubDetail.ts create mode 100644 src/domain/Club/queries.ts delete mode 100644 src/features/Club/queries.ts diff --git a/src/api/types/club.ts b/src/api/types/club.ts index 09461c39..81647c48 100644 --- a/src/api/types/club.ts +++ b/src/api/types/club.ts @@ -1,4 +1,4 @@ -import { CategoryType } from '@/features/Club/constants' +import { CategoryType } from '@/domain/Club/constants' import { ClubInterface } from '@/types/club' export interface ClubProfileProps { @@ -20,7 +20,6 @@ export interface GetClubRequest { export type GetClubResponse = ClubInterface[] export interface PostClubLikeRequest { - queryParams: GetClubRequest clubId: number } diff --git a/src/features/Club/constants.ts b/src/domain/Club/constants.ts similarity index 100% rename from src/features/Club/constants.ts rename to src/domain/Club/constants.ts diff --git a/src/features/Club/hooks/useGetClubSearch.ts b/src/domain/Club/hooks/useGetClubSearch.ts similarity index 95% rename from src/features/Club/hooks/useGetClubSearch.ts rename to src/domain/Club/hooks/useGetClubSearch.ts index 1b388c77..37038957 100644 --- a/src/features/Club/hooks/useGetClubSearch.ts +++ b/src/domain/Club/hooks/useGetClubSearch.ts @@ -1,7 +1,7 @@ import { useQueryClient, useSuspenseQuery } from '@tanstack/react-query' import { GetClubRequest, GetClubResponse } from '@/api/types/club' -import { CLUB_QUERY_KEY } from '@/features/Club/queries' +import { CLUB_QUERY_KEY } from '@/domain/Club/queries' import { ClubSearchParams } from '@/types/club' import { useAuth } from '@/util/auth/useAuth' import { apiInterface } from '@/util/axios/custom-axios' diff --git a/src/features/Club/hooks/usePostClubLike.ts b/src/domain/Club/hooks/usePostClubLike.ts similarity index 50% rename from src/features/Club/hooks/usePostClubLike.ts rename to src/domain/Club/hooks/usePostClubLike.ts index 8d77e11a..3997d58c 100644 --- a/src/features/Club/hooks/usePostClubLike.ts +++ b/src/domain/Club/hooks/usePostClubLike.ts @@ -1,8 +1,8 @@ import { useQueryClient } from '@tanstack/react-query' import { useErrorHandledMutation } from '@/api/hooks/useErrorHandledMutation' -import { GetClubResponse, PostClubLikeRequest, PostClubLikeResponse } from '@/api/types/club' -import { CLUB_QUERY_KEY } from '@/features/Club/queries' +import { PostClubLikeRequest, PostClubLikeResponse } from '@/api/types/club' +import { CLUB_QUERY_KEY } from '@/domain/Club/queries' import { apiInterface } from '@/util/axios/custom-axios' const postClubLike = async ({ clubId }: PostClubLikeRequest) => { @@ -14,15 +14,8 @@ export const usePostClubLike = () => { const queryClient = useQueryClient() return useErrorHandledMutation({ mutationFn: postClubLike, - onSuccess: (response, { queryParams }) => { - queryClient.setQueryData( - CLUB_QUERY_KEY.clubSearchResults({ ...queryParams, keyword: queryParams.keyword }), - oldData => { - if (oldData !== undefined) { - return oldData.map(club => (club.clubId === response.clubId ? response : { ...club })) - } - }, - ) + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: CLUB_QUERY_KEY.base() }) }, }) } diff --git a/src/domain/Club/hooks/useReadClubDetail.ts b/src/domain/Club/hooks/useReadClubDetail.ts new file mode 100644 index 00000000..939c8618 --- /dev/null +++ b/src/domain/Club/hooks/useReadClubDetail.ts @@ -0,0 +1,15 @@ +import { useSuspenseQuery } from '@tanstack/react-query' + +import { useAsyncRead } from '@/common/hooks/useAsyncRead' +import { CLUB_QUERY_KEY } from '@/domain/Club/queries' +import { kuKeyClient } from '@/packages/api' +import { useAuth } from '@/util/auth/useAuth' + +export const useReadClubDetail = (clubId: number) => { + const isLogin = useAuth().authState ?? false + const read = useAsyncRead(kuKeyClient.api.ClubApi.clubClubIdGet) + return useSuspenseQuery({ + queryKey: CLUB_QUERY_KEY.clubDetail(clubId, isLogin), + queryFn: () => read({ clubId, isLogin }), + }) +} diff --git a/src/domain/Club/queries.ts b/src/domain/Club/queries.ts new file mode 100644 index 00000000..e8b4f1a1 --- /dev/null +++ b/src/domain/Club/queries.ts @@ -0,0 +1,7 @@ +import { GetClubRequest } from '@/api/types/club' + +export const CLUB_QUERY_KEY = { + base: () => ['club'] as const, + clubSearchResults: (query: GetClubRequest) => [...CLUB_QUERY_KEY.base(), 'clubSearchResult', query] as const, + clubDetail: (clubID: number, isLogin: boolean) => [...CLUB_QUERY_KEY.base(), 'clubDetail', clubID, isLogin] as const, +} diff --git a/src/features/Club/components/CategoryDrawer/index.tsx b/src/features/Club/components/CategoryDrawer/index.tsx index 568b2020..0f77dde3 100644 --- a/src/features/Club/components/CategoryDrawer/index.tsx +++ b/src/features/Club/components/CategoryDrawer/index.tsx @@ -3,9 +3,9 @@ import { useEffect, useState } from 'react' import * as s from './style.css' +import { CATEGORY_LIST, CategoryType } from '@/domain/Club/constants' +import { useGetCachedClubSearchResult } from '@/domain/Club/hooks/useGetClubSearch' import CategoryChip from '@/features/Club/components/CategoryChip' -import { CATEGORY_LIST, CategoryType } from '@/features/Club/constants' -import { useGetCachedClubSearchResult } from '@/features/Club/hooks/useGetClubSearch' import { ClubSearchParams } from '@/types/club' import { useQueryParams } from '@/util/hooks/useQueryParams' diff --git a/src/features/Club/components/ClubCard/index.tsx b/src/features/Club/components/ClubCard/index.tsx index ed577f6f..9ac08673 100644 --- a/src/features/Club/components/ClubCard/index.tsx +++ b/src/features/Club/components/ClubCard/index.tsx @@ -13,9 +13,8 @@ interface Props { } const ClubCard = ({ clubData, handleLikeClick }: Props) => { return ( - - {/* TODO: 안티패턴!!!!!! state 말고 해당 페이지 내에서 데이터 패칭하기 */} -
+
+
{clubData.name} {
{clubData.description}
-
+ handleLikeClick(clubData.clubId)} + onClick={event => { + event.stopPropagation() + handleLikeClick(clubData.clubId) + }} >
@@ -57,7 +59,7 @@ const ClubCard = ({ clubData, handleLikeClick }: Props) => { } /> - +
) } export default ClubCard diff --git a/src/features/Club/components/ClubCard/style.css.ts b/src/features/Club/components/ClubCard/style.css.ts index 2325df4f..c5bb80c3 100644 --- a/src/features/Club/components/ClubCard/style.css.ts +++ b/src/features/Club/components/ClubCard/style.css.ts @@ -107,8 +107,10 @@ export const HeartIcon = recipe({ width: '1.18rem', color: vars.color.black, transition: 'color 0.25s ease', - marginLeft: '0.22rem', }, + f.smUp({ + marginLeft: '0.22rem', + }), f.smDown({ width: '1.03rem', color: vars.color.darkGray2, diff --git a/src/features/Club/components/ClubList/index.tsx b/src/features/Club/components/ClubList/index.tsx index 0ca54024..485e25fa 100644 --- a/src/features/Club/components/ClubList/index.tsx +++ b/src/features/Club/components/ClubList/index.tsx @@ -5,9 +5,9 @@ import * as s from './style.css' import { Responsive } from '@/common/Responsive' import Toast from '@/components/ui/toast' +import { useGetClubSearch } from '@/domain/Club/hooks/useGetClubSearch' +import { usePostClubLike } from '@/domain/Club/hooks/usePostClubLike' import ClubCard from '@/features/Club/components/ClubCard' -import { useGetClubSearch } from '@/features/Club/hooks/useGetClubSearch' -import { usePostClubLike } from '@/features/Club/hooks/usePostClubLike' import { USER_AUTH_MESSAGE } from '@/lib/messages/common' import { ClubSearchParams } from '@/types/club' import { useAuth } from '@/util/auth/useAuth' @@ -34,10 +34,10 @@ const ClubList = () => { const handleLikeClick = useDeepCompareCallback( (clubId: number) => { - if (isLogin) likeClub({ clubId, queryParams: requestQuery }) + if (isLogin) likeClub({ clubId }) else toast.custom(() => ) }, - [likeClub, query, isLogin], + [likeClub, isLogin], ) return ( diff --git a/src/features/Club/components/DesktopCategorySelector.tsx/index.tsx b/src/features/Club/components/DesktopCategorySelector.tsx/index.tsx index 9adb34b3..9e9b896b 100644 --- a/src/features/Club/components/DesktopCategorySelector.tsx/index.tsx +++ b/src/features/Club/components/DesktopCategorySelector.tsx/index.tsx @@ -1,7 +1,7 @@ import * as s from './style.css' import CategoryChip from '@/features/Club/components/CategoryChip' -import { CATEGORY_LIST, CategoryType } from '@/features/Club/constants' +import { CATEGORY_LIST, CategoryType } from '@/domain/Club/constants' import { ClubSearchParams } from '@/types/club' import { useQueryParams } from '@/util/hooks/useQueryParams' diff --git a/src/features/Club/components/MobileCategorySelector.tsx/index.tsx b/src/features/Club/components/MobileCategorySelector.tsx/index.tsx index 67ccf0c0..82296977 100644 --- a/src/features/Club/components/MobileCategorySelector.tsx/index.tsx +++ b/src/features/Club/components/MobileCategorySelector.tsx/index.tsx @@ -2,7 +2,7 @@ import * as s from './style.css' import OptionIcon from '@/assets/icon/OptionIcon' import CategoryDrawer from '@/features/Club/components/CategoryDrawer' -import { CATEGORY_LIST, CategoryType } from '@/features/Club/constants' +import { CATEGORY_LIST, CategoryType } from '@/domain/Club/constants' import useDrawer from '@/util/hooks/useDrawer' interface Props { diff --git a/src/features/Club/queries.ts b/src/features/Club/queries.ts deleted file mode 100644 index a68286c9..00000000 --- a/src/features/Club/queries.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { GetClubRequest } from '@/api/types/club' - -export const CLUB_QUERY_KEY = { - clubSearchResults: (query: GetClubRequest) => ['clubSearchResult', query], -} diff --git a/src/features/ClubDetail/index.tsx b/src/features/ClubDetail/index.tsx index 6135cd06..cebb734d 100644 --- a/src/features/ClubDetail/index.tsx +++ b/src/features/ClubDetail/index.tsx @@ -1,29 +1,42 @@ import { LinkIcon } from 'lucide-react' -import { useLocation } from 'react-router-dom' +import { useParams } from 'react-router-dom' +import { toast } from 'sonner' import * as s from './style.css' import HeartIcon from '@/assets/icon/HeartIcon' +import Toast from '@/components/ui/toast' +import { usePostClubLike } from '@/domain/Club/hooks/usePostClubLike' +import { useReadClubDetail } from '@/domain/Club/hooks/useReadClubDetail' import ContactButton from '@/features/Club/components/ClubCard/ContactButton' import ClubSchedule from '@/features/ClubSchedule' -import { ClubInterface } from '@/types/club' +import { USER_AUTH_MESSAGE } from '@/lib/messages/common' +import { useAuth } from '@/util/auth/useAuth' +import { useDeepCompareCallback } from '@/util/hooks/useDeepCompare' const ClubDetail = () => { - // const { clubId } = useParams() - // TODO: 추후에 단일 동아리 api 파서 옮기기 - const { clubData } = useLocation().state as { clubData: ClubInterface } + const isLogin = useAuth().authState ?? false + const { clubId } = useParams() + + const { data: clubData } = useReadClubDetail(Number(clubId)) + const { mutate: likeClub } = usePostClubLike() + + const handleLikeClick = useDeepCompareCallback(() => { + if (isLogin) likeClub({ clubId: Number(clubId) }) + else toast.custom(() => ) + }, [likeClub, isLogin]) return (
- {clubData.name} + {clubData.name} {/* TODO: IMG array로 만들기 */}
{clubData.summary}

{clubData.name}

-
diff --git a/src/features/ClubDetail/style.css.ts b/src/features/ClubDetail/style.css.ts index fb75ce28..23d62fbc 100644 --- a/src/features/ClubDetail/style.css.ts +++ b/src/features/ClubDetail/style.css.ts @@ -1,4 +1,5 @@ import { style } from '@vanilla-extract/css' +import { recipe } from '@vanilla-extract/recipes' import { f } from '@/style' import { vars } from '@/theme/theme.css' @@ -74,16 +75,25 @@ export const Title = style([ f.smDown(vars.typography.mobile.display2SB), ]) -export const LikeButton = style([ - f.cursorPointer, - { width: '2.4rem', color: vars.color.lightGray1 }, - f.smUp({ - marginRight: '1.42rem', - }), - f.smDown({ - width: '1.66rem', - }), -]) +export const LikeButton = recipe({ + base: [ + f.cursorPointer, + { width: '2.4rem', color: vars.color.lightGray1, transition: 'color 0.256s' }, + f.smUp({ + marginRight: '1.42rem', + }), + f.smDown({ + width: '1.66rem', + }), + ], + variants: { + clicked: { + true: { + color: vars.color.red3, + }, + }, + }, +}) export const Contents = style([ f.flex, diff --git a/src/types/club.ts b/src/types/club.ts index b8150ced..96cb1855 100644 --- a/src/types/club.ts +++ b/src/types/club.ts @@ -1,4 +1,4 @@ -import { CategoryType } from '@/features/Club/constants' +import { CategoryType } from '@/domain/Club/constants' export interface ClubProfileProps { img: string From e47cdd77235c6645ec7bab90f40f645fbece0383 Mon Sep 17 00:00:00 2001 From: Seungmin Cha <75214259+Virtuso1225@users.noreply.github.com> Date: Sun, 16 Mar 2025 16:33:13 +0900 Subject: [PATCH 2/2] Feat/home club (#216) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat(home): 홈의 동아리와 동아리 상세를 연결해요. * feat: 추천 동아리도 동아리 상세와 연결해요. --- src/features/HomeClubs/components/HotClubs/index.tsx | 6 ++++-- .../HomeClubs/components/RecommendedClubs/index.tsx | 7 ++++++- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/features/HomeClubs/components/HotClubs/index.tsx b/src/features/HomeClubs/components/HotClubs/index.tsx index 25869278..d1645204 100644 --- a/src/features/HomeClubs/components/HotClubs/index.tsx +++ b/src/features/HomeClubs/components/HotClubs/index.tsx @@ -1,12 +1,14 @@ import { motion } from 'framer-motion' +import { useNavigate } from 'react-router-dom' import * as s from '../Clubs/style.css' import { useReadHotClubs } from '@/features/HomeClubs/hooks/useReadHotClubs' import { Typography } from '@/ui/Typography' - const HotClubs = () => { const { data: hotClubs } = useReadHotClubs() + const navigate = useNavigate() + const handleClick = (id: number) => navigate(`/club/detail/${id}`) return ( { transition={{ duration: 0.2, ease: 'easeInOut' }} > {hotClubs.map(item => ( -