diff --git a/src/features/meetings/components/MeetingApprovalItem.tsx b/src/features/meetings/components/MeetingApprovalItem.tsx
index c9fea42..210edd9 100644
--- a/src/features/meetings/components/MeetingApprovalItem.tsx
+++ b/src/features/meetings/components/MeetingApprovalItem.tsx
@@ -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 =
diff --git a/src/features/meetings/components/MeetingDetailButton.tsx b/src/features/meetings/components/MeetingDetailButton.tsx
index 6625064..28bd4ce 100644
--- a/src/features/meetings/components/MeetingDetailButton.tsx
+++ b/src/features/meetings/components/MeetingDetailButton.tsx
@@ -1,5 +1,8 @@
+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'
@@ -7,6 +10,7 @@ interface MeetingDetailButtonProps {
buttonLabel: string
isEnabled: boolean
type: MeetingDetailActionStateType
+ gatheringId: number
meetingId: number
}
@@ -14,8 +18,10 @@ export default function MeetingDetailButton({
buttonLabel,
isEnabled,
type,
+ gatheringId,
meetingId,
}: MeetingDetailButtonProps) {
+ const navigate = useNavigate()
const joinMutation = useJoinMeeting()
const cancelJoinMutation = useCancelJoinMeeting()
const { openError, openConfirm } = useGlobalModalStore()
@@ -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
}
diff --git a/src/features/meetings/components/MeetingDetailInfo.tsx b/src/features/meetings/components/MeetingDetailInfo.tsx
index 3710ea6..1dbc594 100644
--- a/src/features/meetings/components/MeetingDetailInfo.tsx
+++ b/src/features/meetings/components/MeetingDetailInfo.tsx
@@ -36,8 +36,11 @@ export function MeetingDetailInfo({ meeting }: MeetingDetailInfoProps) {
{/* 도서 */}
- 도서
- -
-
{meeting.book.bookName}
+ -
+
+
{meeting.book.bookName}
+
{meeting.book.authors}
+

{
+export const useConfirmMeeting = (gatheringId: number) => {
const queryClient = useQueryClient()
return useMutation
, ApiError, number>({
@@ -33,6 +34,8 @@ export const useConfirmMeeting = () => {
queryClient.invalidateQueries({
queryKey: meetingQueryKeys.approvals(),
})
+ // 모임 약속 리스트 캐시 무효화
+ queryClient.invalidateQueries({ queryKey: gatheringQueryKeys.meetings(gatheringId) })
},
})
}
diff --git a/src/features/meetings/hooks/useMeetingForm.ts b/src/features/meetings/hooks/useMeetingForm.ts
index 060dda7..a1ec808 100644
--- a/src/features/meetings/hooks/useMeetingForm.ts
+++ b/src/features/meetings/hooks/useMeetingForm.ts
@@ -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 = '* 도서를 선택해주세요.'
}
diff --git a/src/features/meetings/meetings.mock.ts b/src/features/meetings/meetings.mock.ts
index 65191b2..c6f7be4 100644
--- a/src/features/meetings/meetings.mock.ts
+++ b/src/features/meetings/meetings.mock.ts
@@ -261,7 +261,7 @@ const mockMeetingDetails: Record = {
buttonLabel: '약속이 끝났어요',
enabled: false,
},
- confirmedTopicExpand: true,
+ confirmedTopic: true,
confirmedTopicDate: '2026-01-20T14:00:00',
},
11: {
@@ -269,7 +269,7 @@ const mockMeetingDetails: Record = {
progressStatus: 'PRE',
meetingName: '킥오프 모임',
meetingStatus: 'CONFIRMED',
- confirmedTopicExpand: false,
+ confirmedTopic: false,
confirmedTopicDate: null,
gathering: {
gatheringId: 102,
diff --git a/src/features/meetings/meetings.types.ts b/src/features/meetings/meetings.types.ts
index e3d380c..8d64d6b 100644
--- a/src/features/meetings/meetings.types.ts
+++ b/src/features/meetings/meetings.types.ts
@@ -215,7 +215,7 @@ export type GetMeetingDetailResponse = {
/** 약속 진행 상태 */
progressStatus: MeetingProgressStatus
/** 주제 확정 여부 */
- confirmedTopicExpand: boolean
+ confirmedTopic: boolean
/** 주제 확정 일시 */
confirmedTopicDate: string | null
/** 모임 정보 */
diff --git a/src/features/topics/components/ProposedTopicList.tsx b/src/features/topics/components/ProposedTopicList.tsx
index 08c1135..d3ba223 100644
--- a/src/features/topics/components/ProposedTopicList.tsx
+++ b/src/features/topics/components/ProposedTopicList.tsx
@@ -12,6 +12,7 @@ type ProposedTopicListProps = {
onLoadMore: () => void
gatheringId: number
meetingId: number
+ confirmedTopic: boolean
}
export default function ProposedTopicList({
@@ -21,6 +22,7 @@ export default function ProposedTopicList({
onLoadMore,
gatheringId,
meetingId,
+ confirmedTopic,
}: ProposedTopicListProps) {
// 무한 스크롤: IntersectionObserver로 다음 페이지 로드
const observerRef = useInfiniteScroll(onLoadMore, {
@@ -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}
/>
))}
diff --git a/src/features/topics/components/TopicHeader.tsx b/src/features/topics/components/TopicHeader.tsx
index 7d9c698..fdf7070 100644
--- a/src/features/topics/components/TopicHeader.tsx
+++ b/src/features/topics/components/TopicHeader.tsx
@@ -64,13 +64,12 @@ export default function TopicHeader(props: TopicHeaderProps) {
)}
- {props.actions.canSuggest && (
-
- )}
+
)}
diff --git a/src/features/topics/hooks/useConfirmTopics.ts b/src/features/topics/hooks/useConfirmTopics.ts
index ca2a2c7..ea8e036 100644
--- a/src/features/topics/hooks/useConfirmTopics.ts
+++ b/src/features/topics/hooks/useConfirmTopics.ts
@@ -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'
@@ -55,6 +56,10 @@ export const useConfirmTopics = () => {
meetingId: variables.meetingId,
}),
})
+ // 약속 상세 무효화
+ queryClient.invalidateQueries({
+ queryKey: meetingQueryKeys.detail(variables.meetingId),
+ })
},
})
}
diff --git a/src/features/topics/topics.api.ts b/src/features/topics/topics.api.ts
index 926758d..6f1abf6 100644
--- a/src/features/topics/topics.api.ts
+++ b/src/features/topics/topics.api.ts
@@ -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(TOPICS_ENDPOINTS.CONFIRM(gatheringId, meetingId), {
+ return api.patch(TOPICS_ENDPOINTS.CONFIRM(gatheringId, meetingId), {
topicIds,
})
}
diff --git a/src/pages/Meetings/MeetingCreatePage.tsx b/src/pages/Meetings/MeetingCreatePage.tsx
index 77dbb03..8b28f84 100644
--- a/src/pages/Meetings/MeetingCreatePage.tsx
+++ b/src/pages/Meetings/MeetingCreatePage.tsx
@@ -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) => {
diff --git a/src/pages/Meetings/MeetingDetailPage.tsx b/src/pages/Meetings/MeetingDetailPage.tsx
index aec2a2d..09e3b7d 100644
--- a/src/pages/Meetings/MeetingDetailPage.tsx
+++ b/src/pages/Meetings/MeetingDetailPage.tsx
@@ -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}
/>
>
@@ -145,7 +146,7 @@ export default function MeetingDetailPage() {
page.items
)}
+ confirmedTopic={meeting?.confirmedTopic ?? false}
hasNextPage={hasNextProposedPage}
isFetchingNextPage={isFetchingNextProposedPage}
onLoadMore={fetchNextProposedPage}
@@ -174,7 +176,7 @@ export default function MeetingDetailPage() {