diff --git a/src/api/book-club/bookClubMainAPI.ts b/src/api/book-club/bookClubMainAPI.ts index 5eafb190..e5dce58f 100644 --- a/src/api/book-club/bookClubMainAPI.ts +++ b/src/api/book-club/bookClubMainAPI.ts @@ -10,7 +10,7 @@ export const bookClubMainAPI = { //북클럽 목록 조회 getBookClubs: async (params?: BookClubParams) => { - await apiClient.get('/book-clubs', { params }); + return await apiClient.get('/book-clubs', { params }); }, //단일 북클럽 조회 diff --git a/src/components/common-layout/FilterBar.tsx b/src/components/common-layout/FilterBar.tsx index 2c8b24d5..3f650976 100644 --- a/src/components/common-layout/FilterBar.tsx +++ b/src/components/common-layout/FilterBar.tsx @@ -10,6 +10,7 @@ interface FilterBarProps { filters: BookClubParams; handleFilterChange: (newFilter: Partial) => void; bookClubs: BookClub[]; + initialBookClubs: BookClub[]; setBookClubs: Dispatch>; } @@ -17,6 +18,7 @@ function FilterBar({ filters, handleFilterChange, bookClubs, + initialBookClubs, setBookClubs, }: FilterBarProps) { return ( @@ -30,6 +32,7 @@ function FilterBar({ /> diff --git a/src/components/common-layout/FilterSection.tsx b/src/components/common-layout/FilterSection.tsx index c024f7d2..adda2138 100644 --- a/src/components/common-layout/FilterSection.tsx +++ b/src/components/common-layout/FilterSection.tsx @@ -2,39 +2,65 @@ import DropDown from '@/components/drop-down/DropDown'; import FilterCheckbox from '@/components/filter-checkbox/FilterCheckbox'; -import { ChangeEvent, Dispatch, SetStateAction, useState } from 'react'; +import { + ChangeEvent, + Dispatch, + SetStateAction, + // useEffect, + useState, +} from 'react'; import SortingButton from '@/components/sorting-button/SortingButton'; import { BookClub, BookClubParams } from '../../types/bookclubs'; import { getMeetingType, getMemberLimit } from '@/lib/utils/filterUtils'; +import { clubStatus } from '@/lib/utils/clubUtils'; interface CategoryTabsProps { bookClubs: BookClub[]; + initialBookClubs: BookClub[]; setBookClubs: Dispatch>; onFilterChange: (newFilters: Partial) => void; } function FilterSection({ bookClubs, + initialBookClubs, setBookClubs, onFilterChange, }: CategoryTabsProps) { const [showAvailableOnly, setShowAvailableOnly] = useState(false); // 신청가능 + // useEffect(() => { + // console.log('체크 상태: ', showAvailableOnly); + // console.log('신청 가능 모임: ', bookClubs); + // }); + const toggleAvailableOnly = (e: ChangeEvent) => { const isChecked = e.target.checked; setShowAvailableOnly(isChecked); const filteredBookClubs = isChecked - ? bookClubs.filter((club) => club.memberCount < club.memberLimit) - : bookClubs; + ? bookClubs.filter( + (club) => + club.memberCount < club.memberLimit && + clubStatus( + club.memberCount, + club.memberLimit, + club.endDate, + new Date(), + ) !== 'closed', + ) + : initialBookClubs; setBookClubs(filteredBookClubs); }; const updateMemberLimitFilter = (selectedValue: string | undefined) => { const memberLimit = getMemberLimit(selectedValue); - if (selectedValue !== undefined) { - onFilterChange({ memberLimit }); + if (memberLimit) { + onFilterChange({ + memberLimitMin: memberLimit.min, + memberLimitMax: memberLimit.max, + }); } }; diff --git a/src/features/bookclub/components/BookClubMainPage.tsx b/src/features/bookclub/components/BookClubMainPage.tsx index 92ce900c..8d027d28 100644 --- a/src/features/bookclub/components/BookClubMainPage.tsx +++ b/src/features/bookclub/components/BookClubMainPage.tsx @@ -6,10 +6,17 @@ import { useAuthStore } from '@/store/authStore'; import ClubListSection from './ClubListSection'; import Button from '@/components/button/Button'; import { useRouter } from 'next/navigation'; +import Loading from '@/components/loading/Loading'; function BookClubMainPage() { - // 커스텀 훅에서 상태와 핸들러 가져오기 - const { bookClubs, setBookClubs, filters, updateFilters } = useBookClubList(); + const { + clubList, + initialBookClubs, + setClubList, + isLoading, + filters, + updateFilters, + } = useBookClubList(); const router = useRouter(); @@ -44,10 +51,17 @@ function BookClubMainPage() { - + {isLoading ? ( +
+ +
+ ) : ( + + )} ); } diff --git a/src/features/bookclub/components/ClubListSection.tsx b/src/features/bookclub/components/ClubListSection.tsx index 999fcea1..9c7a6ee9 100644 --- a/src/features/bookclub/components/ClubListSection.tsx +++ b/src/features/bookclub/components/ClubListSection.tsx @@ -7,6 +7,7 @@ import { useMemo } from 'react'; import EmptyState from '@/components/common-layout/EmptyState'; import { clubStatus } from '@/lib/utils/clubUtils'; import { BookClub } from '@/types/bookclubs'; +import { useLikeClub, useUnLikeClub } from '@/lib/hooks'; interface ClubListSectionProps { bookClubs: BookClub[]; @@ -14,9 +15,15 @@ interface ClubListSectionProps { function ClubListSection({ bookClubs = [] }: ClubListSectionProps) { const router = useRouter(); + const { onConfirmUnLike } = useUnLikeClub(); + const { onConfirmLike } = useLikeClub(); const today = useMemo(() => new Date(), []); + const handleLikeClub = (isLiked: boolean, id: number) => { + isLiked ? onConfirmUnLike(id) : onConfirmLike(id); + }; + return (
{bookClubs?.length > 0 ? ( @@ -32,8 +39,8 @@ function ClubListSection({ bookClubs = [] }: ClubListSectionProps) { isLiked={club.isLiked} current={club.memberCount} max={club.memberLimit} - isPast={isPastDate(club.endDate, today)} // 지난 모임 여부 - isCanceled={false} // 모임 취소 여부 (API 값에 따라 변경 가능) + isPast={isPastDate(club.targetDate, today)} // 지난 모임 여부 + isCanceled={club.isInactive} // 모임 취소 여부 bookClubType={club.bookClubType} meetingType={club.meetingType} clubStatus={clubStatus( @@ -42,9 +49,8 @@ function ClubListSection({ bookClubs = [] }: ClubListSectionProps) { club.endDate, today, )} - onLikeClick={() => console.log(`${club.title} 좋아요 클릭`)} + onLikeClick={() => handleLikeClub(club.isLiked, club.id)} onClick={() => router.push(`/bookclub/${club.id}`)} - onDelete={() => console.log(`${club.title} 삭제 클릭`)} /> )) ) : ( diff --git a/src/features/bookclub/hooks/useFetchBookClubList.ts b/src/features/bookclub/hooks/useFetchBookClubList.ts index c7fd8b99..1f150f5d 100644 --- a/src/features/bookclub/hooks/useFetchBookClubList.ts +++ b/src/features/bookclub/hooks/useFetchBookClubList.ts @@ -1,11 +1,12 @@ -import { useState, useEffect, useCallback } from 'react'; -import { getBookClubs } from '../api/bookclubApi'; +import { useState, useEffect } from 'react'; import { BookClub, BookClubParams } from '@/types/bookclubs'; +import { useQuery } from '@tanstack/react-query'; +import { bookClubs } from '@/api/book-club/react-query'; const useBookClubList = () => { - const [bookClubs, setBookClubs] = useState([]); - const [loading, setLoading] = useState(false); - const [error, setError] = useState(null); + // TODO: 신청 가능 필터 param 추가시 clubList, initialBookClubs 상태 관리x + const [clubList, setClubList] = useState([]); + const [initialBookClubs, setInitialBookClubs] = useState([]); const [filters, setFilters] = useState({ bookClubType: 'ALL', meetingType: 'ALL', @@ -15,31 +16,29 @@ const useBookClubList = () => { searchKeyword: '', }); - const fetchData = useCallback(async () => { - setLoading(true); - setError(null); - try { - const data = await getBookClubs(filters); // API 호출 - setBookClubs(data); - } catch (err) { - setError(err as Error); - } finally { - setLoading(false); - } - }, [filters]); + const { data, isLoading, error } = useQuery({ + ...bookClubs.all(filters), + }); + const clubInfo = data?.data.bookClubs; + + // TODO: param 추가시, useEffect 대신 clubInfo 직접 사용 useEffect(() => { - fetchData(); - }, [fetchData]); + if (clubInfo) { + setClubList(clubInfo); + setInitialBookClubs(clubInfo); // 초기 데이터 설정 + } + }, [clubInfo]); const updateFilters = (newFilters: Partial) => { setFilters((prevFilters) => ({ ...prevFilters, ...newFilters })); }; return { - bookClubs, - setBookClubs, - loading, + clubList, + initialBookClubs, + setClubList, + isLoading, error, filters, updateFilters, diff --git a/src/features/club-wish/components/WishPage.tsx b/src/features/club-wish/components/WishPage.tsx index 6cb3f6ac..272aa81b 100644 --- a/src/features/club-wish/components/WishPage.tsx +++ b/src/features/club-wish/components/WishPage.tsx @@ -23,6 +23,7 @@ function WishPage() { handleFilterChange={() => {}} bookClubs={mockBookClubs} setBookClubs={() => {}} + initialBookClubs={[]} /> diff --git a/src/lib/utils/filterUtils.ts b/src/lib/utils/filterUtils.ts index 38619435..6e459676 100644 --- a/src/lib/utils/filterUtils.ts +++ b/src/lib/utils/filterUtils.ts @@ -2,16 +2,16 @@ import { BookClubParams } from '@/types/bookclubs'; export const getMemberLimit = (selectedValue: string | undefined) => { switch (selectedValue) { - case 'TWO_FOUR': - return 4; - case 'FIVE_SEVEN': - return 7; - case 'EIGHT_TEN': - return 10; - case 'OVER_ELEVEN': - return 11; + case 'THREE_FIVE': + return { min: 3, max: 5 }; + case 'SIX_EIGHT': + return { min: 6, max: 8 }; + case 'NINE_ELEVEN': + return { min: 9, max: 11 }; + case 'TWELVE': + return { min: 12, max: 20 }; default: - return undefined; + return { min: 3, max: 20 }; } }; diff --git a/src/types/bookclubs.ts b/src/types/bookclubs.ts index 8a9c97f1..11337c3b 100644 --- a/src/types/bookclubs.ts +++ b/src/types/bookclubs.ts @@ -8,6 +8,8 @@ export interface BookClubParams { page?: number; size?: number; searchKeyword?: string; + memberLimitMin?: number; + memberLimitMax?: number; } export interface MyProfileParams {