Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/features/meetings/components/MeetingApprovalItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export default function MeetingApprovalItem({ item, gatheringId }: MeetingApprov
const { meetingName, bookName, nickname, startDateTime, endDateTime, meetingStatus, meetingId } =
item

const confirmMutation = useConfirmMeeting()
const confirmMutation = useConfirmMeeting(gatheringId)
const rejectMutation = useRejectMeeting(gatheringId)
const deleteMutation = useDeleteMeeting(gatheringId)
const isPending =
Expand Down
10 changes: 8 additions & 2 deletions src/features/meetings/components/MeetingDetailButton.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,27 @@
import { useNavigate } from 'react-router-dom'

import { useCancelJoinMeeting, useJoinMeeting } from '@/features/meetings/hooks'
import type { MeetingDetailActionStateType } from '@/features/meetings/meetings.types'
import { ROUTES } from '@/shared/constants/routes'
import { Button } from '@/shared/ui'
import { useGlobalModalStore } from '@/store'

interface MeetingDetailButtonProps {
buttonLabel: string
isEnabled: boolean
type: MeetingDetailActionStateType
gatheringId: number
meetingId: number
}

export default function MeetingDetailButton({
buttonLabel,
isEnabled,
type,
gatheringId,
meetingId,
}: MeetingDetailButtonProps) {
const navigate = useNavigate()
const joinMutation = useJoinMeeting()
const cancelJoinMutation = useCancelJoinMeeting()
const { openError, openConfirm } = useGlobalModalStore()
Expand All @@ -25,9 +31,9 @@ export default function MeetingDetailButton({
const handleClick = async () => {
if (!isEnabled || isPending) return

// 약속 수정 - 페이지 이동 예정 (TODO)
// 약속 수정
if (type === 'CAN_EDIT') {
// 페이지 이동 로직 추가 예정
navigate(ROUTES.MEETING_UPDATE(gatheringId, meetingId))
return
}

Expand Down
7 changes: 5 additions & 2 deletions src/features/meetings/components/MeetingDetailInfo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,11 @@ export function MeetingDetailInfo({ meeting }: MeetingDetailInfoProps) {
{/* 도서 */}
<dl className="flex gap-base">
<dt className={DT_VARIANTS}>도서</dt>
<dd className="flex flex-col gap-xtiny">
<p className="text-black typo-body3">{meeting.book.bookName}</p>
<dd className="flex flex-col gap-tiny">
<div className="flex flex-col gap-xtiny">
<p className="text-black typo-body3">{meeting.book.bookName}</p>
<p className="typo-caption1 text-grey-700">{meeting.book.authors}</p>
</div>
<div className="w-[120px] h-[170px] overflow-hidden rounded">
<img
src={meeting.book.thumbnail}
Expand Down
11 changes: 7 additions & 4 deletions src/features/meetings/hooks/useConfirmMeeting.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@

import { useMutation, useQueryClient } from '@tanstack/react-query'

import { ApiError } from '@/api/errors'
import type { ApiResponse } from '@/api/types'
import type { ApiError, ApiResponse } from '@/api'
import { gatheringQueryKeys } from '@/features/gatherings'
import { confirmMeeting, type ConfirmMeetingResponse } from '@/features/meetings'

import { meetingQueryKeys } from './meetingQueryKeys'
Expand All @@ -18,12 +18,13 @@ import { meetingQueryKeys } from './meetingQueryKeys'
* 약속을 승인하고 관련 쿼리 캐시를 무효화합니다.
* - 약속 승인 리스트 캐시 무효화
* - 약속 승인 카운트 캐시 무효화
* - 모임 약속 리스트 캐시 무효화
*
* @example
* const confirmMutation = useConfirmMeeting()
* const confirmMutation = useConfirmMeeting(gatheringId)
* confirmMutation.mutate(meetingId)
*/
export const useConfirmMeeting = () => {
export const useConfirmMeeting = (gatheringId: number) => {
const queryClient = useQueryClient()

return useMutation<ApiResponse<ConfirmMeetingResponse>, ApiError, number>({
Expand All @@ -33,6 +34,8 @@ export const useConfirmMeeting = () => {
queryClient.invalidateQueries({
queryKey: meetingQueryKeys.approvals(),
})
// 모임 약속 리스트 캐시 무효화
queryClient.invalidateQueries({ queryKey: gatheringQueryKeys.meetings(gatheringId) })
},
})
}
11 changes: 6 additions & 5 deletions src/features/meetings/hooks/useMeetingForm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,11 +123,12 @@ export const useMeetingForm = ({ gatheringMaxCount, initialData }: UseMeetingFor
const newError: ValidationErrors = {}

if (
!formData.bookId ||
!formData.bookName ||
!formData.bookThumbnail ||
!formData.bookAuthors ||
!formData.bookPublisher
!isEditMode &&
(!formData.bookId ||
!formData.bookName ||
!formData.bookThumbnail ||
!formData.bookAuthors ||
!formData.bookPublisher)
) {
newError.bookId = '* 도서를 선택해주세요.'
}
Expand Down
4 changes: 2 additions & 2 deletions src/features/meetings/meetings.mock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -261,15 +261,15 @@ const mockMeetingDetails: Record<number, GetMeetingDetailResponse> = {
buttonLabel: '약속이 끝났어요',
enabled: false,
},
confirmedTopicExpand: true,
confirmedTopic: true,
confirmedTopicDate: '2026-01-20T14:00:00',
},
11: {
meetingId: 11,
progressStatus: 'PRE',
meetingName: '킥오프 모임',
meetingStatus: 'CONFIRMED',
confirmedTopicExpand: false,
confirmedTopic: false,
confirmedTopicDate: null,
gathering: {
gatheringId: 102,
Expand Down
2 changes: 1 addition & 1 deletion src/features/meetings/meetings.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@ export type GetMeetingDetailResponse = {
/** 약속 진행 상태 */
progressStatus: MeetingProgressStatus
/** 주제 확정 여부 */
confirmedTopicExpand: boolean
confirmedTopic: boolean
/** 주제 확정 일시 */
confirmedTopicDate: string | null
/** 모임 정보 */
Expand Down
7 changes: 5 additions & 2 deletions src/features/topics/components/ProposedTopicList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ type ProposedTopicListProps = {
onLoadMore: () => void
gatheringId: number
meetingId: number
confirmedTopic: boolean
}

export default function ProposedTopicList({
Expand All @@ -21,6 +22,7 @@ export default function ProposedTopicList({
onLoadMore,
gatheringId,
meetingId,
confirmedTopic,
}: ProposedTopicListProps) {
// 무한 스크롤: IntersectionObserver로 다음 페이지 로드
const observerRef = useInfiniteScroll(onLoadMore, {
Expand All @@ -44,11 +46,12 @@ export default function ProposedTopicList({
description={topic.description}
createdByNickname={topic.createdByInfo.nickname}
likeCount={topic.likeCount}
isLiked={topic.isLiked}
canDelete={topic.canDelete}
isLiked={confirmedTopic ? false : topic.isLiked}
canDelete={confirmedTopic ? false : topic.canDelete}
gatheringId={gatheringId}
meetingId={meetingId}
topicId={topic.topicId}
isLikeDisabled={confirmedTopic}
/>
</li>
))}
Expand Down
13 changes: 6 additions & 7 deletions src/features/topics/components/TopicHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -64,13 +64,12 @@ export default function TopicHeader(props: TopicHeaderProps) {
</Button>
)}

{props.actions.canSuggest && (
<Button
onClick={() => navigate(ROUTES.TOPICS_CREATE(props.gatheringId, props.meetingId))}
>
제안하기
</Button>
)}
<Button
onClick={() => navigate(ROUTES.TOPICS_CREATE(props.gatheringId, props.meetingId))}
disabled={!props.actions.canSuggest}
>
제안하기
</Button>
</div>
</div>
)}
Expand Down
5 changes: 5 additions & 0 deletions src/features/topics/hooks/useConfirmTopics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import { useMutation, useQueryClient } from '@tanstack/react-query'

import { ApiError } from '@/api/errors'
import { meetingQueryKeys } from '@/features/meetings/hooks/meetingQueryKeys'

import { confirmTopics } from '../topics.api'
import type { ConfirmTopicsParams, ConfirmTopicsResponse } from '../topics.types'
Expand Down Expand Up @@ -55,6 +56,10 @@ export const useConfirmTopics = () => {
meetingId: variables.meetingId,
}),
})
// 약속 상세 무효화
queryClient.invalidateQueries({
queryKey: meetingQueryKeys.detail(variables.meetingId),
})
},
})
}
4 changes: 2 additions & 2 deletions src/features/topics/topics.api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -230,14 +230,14 @@ export const confirmTopics = async (

// 🚧 임시: 로그인 기능 개발 전까지 목데이터 사용
// TODO: 로그인 완료 후 아래 주석을 해제하고 목데이터 로직 제거
if (USE_MOCK_DATA) {
if (USE_MOCK) {
// 실제 API 호출을 시뮬레이션하기 위한 지연
await new Promise((resolve) => setTimeout(resolve, 500))
return getMockConfirmTopics(meetingId, topicIds)
}

// 실제 API 호출 (로그인 완료 후 사용)
return api.post<ConfirmTopicsResponse>(TOPICS_ENDPOINTS.CONFIRM(gatheringId, meetingId), {
return api.patch<ConfirmTopicsResponse>(TOPICS_ENDPOINTS.CONFIRM(gatheringId, meetingId), {
topicIds,
})
}
2 changes: 1 addition & 1 deletion src/pages/Meetings/MeetingCreatePage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ export default function MeetingCreatePage() {
{
onSuccess: () => {
openAlert('약속 수정 완료', '약속이 성공적으로 수정되었습니다.', () => {
navigate(ROUTES.GATHERING_DETAIL(gatheringId), { replace: true })
navigate(ROUTES.MEETING_DETAIL(gatheringId, id), { replace: true })
})
},
onError: (error) => {
Expand Down
6 changes: 4 additions & 2 deletions src/pages/Meetings/MeetingDetailPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ export default function MeetingDetailPage() {
buttonLabel={meeting.actionState.buttonLabel}
isEnabled={meeting.actionState.enabled}
type={meeting.actionState.type}
gatheringId={Number(gatheringId)}
meetingId={meeting.meetingId}
/>
</>
Expand Down Expand Up @@ -145,7 +146,7 @@ export default function MeetingDetailPage() {
<div className="flex flex-col gap-base">
<TopicHeader
activeTab="PROPOSED"
confirmedTopic={meeting?.confirmedTopicExpand ?? false}
confirmedTopic={meeting?.confirmedTopic ?? false}
actions={proposedTopicsInfiniteData.pages[0].actions}
confirmedTopicDate={meeting?.confirmedTopicDate ?? null}
proposedTopicsCount={proposedTopicsInfiniteData.pages[0].totalCount ?? 0}
Expand All @@ -157,6 +158,7 @@ export default function MeetingDetailPage() {
topics={proposedTopicsInfiniteData.pages.flatMap(
(page: GetProposedTopicsResponse) => page.items
)}
confirmedTopic={meeting?.confirmedTopic ?? false}
hasNextPage={hasNextProposedPage}
isFetchingNextPage={isFetchingNextProposedPage}
onLoadMore={fetchNextProposedPage}
Expand All @@ -174,7 +176,7 @@ export default function MeetingDetailPage() {
<div className="flex flex-col gap-base">
<TopicHeader
activeTab="CONFIRMED"
confirmedTopic={meeting?.confirmedTopicExpand ?? false}
confirmedTopic={meeting?.confirmedTopic ?? false}
actions={confirmedTopicsInfiniteData.pages[0].actions}
confirmedTopicDate={meeting?.confirmedTopicDate ?? null}
/>
Expand Down
Loading