diff --git a/src/features/preOpinions/components/PreOpinionDetail.tsx b/src/features/pre-opinion/components/PreOpinionDetail.tsx similarity index 96% rename from src/features/preOpinions/components/PreOpinionDetail.tsx rename to src/features/pre-opinion/components/PreOpinionDetail.tsx index 2339bab..613434e 100644 --- a/src/features/preOpinions/components/PreOpinionDetail.tsx +++ b/src/features/pre-opinion/components/PreOpinionDetail.tsx @@ -5,12 +5,12 @@ import { Chip } from '@/shared/ui/Chip' import { useGlobalModalStore } from '@/store' import { useDeleteMyPreOpinionAnswer } from '../hooks/useDeleteMyPreOpinionAnswer' -import { ROLE_TO_AVATAR_VARIANT } from '../preOpinions.constants' -import type { PreOpinionMember, PreOpinionTopic } from '../preOpinions.types' +import { ROLE_TO_AVATAR_VARIANT } from '../preOpinion.constants' +import type { PreOpinionAnswerTopic, PreOpinionMember } from '../preOpinion.types' type PreOpinionDetailProps = { member: PreOpinionMember - topics: PreOpinionTopic[] + topics: PreOpinionAnswerTopic[] gatheringId: number meetingId: number } diff --git a/src/features/preOpinions/components/PreOpinionMemberList.tsx b/src/features/pre-opinion/components/PreOpinionMemberList.tsx similarity index 91% rename from src/features/preOpinions/components/PreOpinionMemberList.tsx rename to src/features/pre-opinion/components/PreOpinionMemberList.tsx index 26e843d..a95990a 100644 --- a/src/features/preOpinions/components/PreOpinionMemberList.tsx +++ b/src/features/pre-opinion/components/PreOpinionMemberList.tsx @@ -1,7 +1,7 @@ import { UserChip } from '@/shared/ui/UserChip' -import { ROLE_TO_AVATAR_VARIANT } from '../preOpinions.constants' -import type { PreOpinionMember } from '../preOpinions.types' +import { ROLE_TO_AVATAR_VARIANT } from '../preOpinion.constants' +import type { PreOpinionMember } from '../preOpinion.types' type PreOpinionMemberListProps = { members: PreOpinionMember[] diff --git a/src/features/pre-opinion/components/PreOpinionWriteHeader.tsx b/src/features/pre-opinion/components/PreOpinionWriteHeader.tsx index 9975621..e9d163c 100644 --- a/src/features/pre-opinion/components/PreOpinionWriteHeader.tsx +++ b/src/features/pre-opinion/components/PreOpinionWriteHeader.tsx @@ -1,6 +1,5 @@ -import { useEffect, useRef, useState } from 'react' - import type { PreOpinionBook } from '@/features/pre-opinion/preOpinion.types' +import { useScrollShadow } from '@/shared/hooks' import { cn } from '@/shared/lib/utils' import { Button } from '@/shared/ui' @@ -42,62 +41,43 @@ const PreOpinionWriteHeader = ({ isSubmitting, isReviewValid = false, }: PreOpinionWriteHeaderProps) => { - const sentinelRef = useRef(null) - const [isStuck, setIsStuck] = useState(false) - - useEffect(() => { - const sentinel = sentinelRef.current - if (!sentinel) return - - const observer = new IntersectionObserver( - ([entry]) => { - setIsStuck(!entry.isIntersecting) - }, - { threshold: 0 } - ) - - observer.observe(sentinel) - return () => observer.disconnect() - }, []) + const isScrolled = useScrollShadow() return ( - <> -
-
-
-
-
-

사전 의견 작성하기

-

- {book.title} · {book.author} -

-
-
- {updatedAt && ( -

{formatUpdatedAt(updatedAt)}

- )} - - -
+
+
+
+
+

사전 의견 작성하기

+

+ {book.title} · {book.author} +

+
+
+ {updatedAt && ( +

{formatUpdatedAt(updatedAt)}

+ )} + +
- +
) } diff --git a/src/features/pre-opinion/components/index.ts b/src/features/pre-opinion/components/index.ts new file mode 100644 index 0000000..6d58f29 --- /dev/null +++ b/src/features/pre-opinion/components/index.ts @@ -0,0 +1,5 @@ +export * from './BookReviewSection' +export * from './PreOpinionDetail' +export * from './PreOpinionMemberList' +export * from './PreOpinionWriteHeader' +export * from './TopicItem' diff --git a/src/features/pre-opinion/hooks/index.ts b/src/features/pre-opinion/hooks/index.ts index 46f7dec..6aaa404 100644 --- a/src/features/pre-opinion/hooks/index.ts +++ b/src/features/pre-opinion/hooks/index.ts @@ -1,4 +1,6 @@ export * from './preOpinionQueryKeys' +export * from './useDeleteMyPreOpinionAnswer' export * from './usePreOpinion' +export * from './usePreOpinionAnswers' export * from './useSavePreOpinion' export * from './useSubmitPreOpinion' diff --git a/src/features/pre-opinion/hooks/preOpinionQueryKeys.ts b/src/features/pre-opinion/hooks/preOpinionQueryKeys.ts index 5d5057f..f52a3d6 100644 --- a/src/features/pre-opinion/hooks/preOpinionQueryKeys.ts +++ b/src/features/pre-opinion/hooks/preOpinionQueryKeys.ts @@ -3,11 +3,26 @@ * @description 사전 의견 관련 Query Key Factory */ -import type { GetPreOpinionParams } from '@/features/pre-opinion/preOpinion.types' +import type { + GetPreOpinionAnswersParams, + GetPreOpinionParams, +} from '@/features/pre-opinion/preOpinion.types' +/** + * Query Key Factory + * + * @description + * 사전 의견 관련 Query Key를 일관되게 관리하기 위한 팩토리 함수 + */ export const preOpinionQueryKeys = { all: ['preOpinions'] as const, + // 내 사전 의견 작성/조회 관련 details: () => [...preOpinionQueryKeys.all, 'detail'] as const, detail: (params: GetPreOpinionParams) => [...preOpinionQueryKeys.details(), params] as const, + + // 사전 의견 목록 관련 + answers: () => [...preOpinionQueryKeys.all, 'answers'] as const, + answerList: (params: GetPreOpinionAnswersParams) => + [...preOpinionQueryKeys.answers(), params] as const, } diff --git a/src/features/preOpinions/hooks/useDeleteMyPreOpinionAnswer.ts b/src/features/pre-opinion/hooks/useDeleteMyPreOpinionAnswer.ts similarity index 92% rename from src/features/preOpinions/hooks/useDeleteMyPreOpinionAnswer.ts rename to src/features/pre-opinion/hooks/useDeleteMyPreOpinionAnswer.ts index 555376d..bc89faa 100644 --- a/src/features/preOpinions/hooks/useDeleteMyPreOpinionAnswer.ts +++ b/src/features/pre-opinion/hooks/useDeleteMyPreOpinionAnswer.ts @@ -7,8 +7,8 @@ import { useMutation, useQueryClient } from '@tanstack/react-query' import type { ApiError } from '@/api' -import { deleteMyPreOpinionAnswer } from '../preOpinions.api' -import type { DeleteMyPreOpinionAnswerParams } from '../preOpinions.types' +import { deleteMyPreOpinionAnswer } from '../preOpinion.api' +import type { DeleteMyPreOpinionAnswerParams } from '../preOpinion.types' import { preOpinionQueryKeys } from './preOpinionQueryKeys' /** diff --git a/src/features/preOpinions/hooks/usePreOpinionAnswers.ts b/src/features/pre-opinion/hooks/usePreOpinionAnswers.ts similarity index 82% rename from src/features/preOpinions/hooks/usePreOpinionAnswers.ts rename to src/features/pre-opinion/hooks/usePreOpinionAnswers.ts index 2c0860b..28a5542 100644 --- a/src/features/preOpinions/hooks/usePreOpinionAnswers.ts +++ b/src/features/pre-opinion/hooks/usePreOpinionAnswers.ts @@ -7,8 +7,8 @@ import { useQuery } from '@tanstack/react-query' import type { ApiError } from '@/api' -import { getPreOpinionAnswers } from '../preOpinions.api' -import type { GetPreOpinionAnswersParams, PreOpinionAnswersData } from '../preOpinions.types' +import { getPreOpinionAnswers } from '../preOpinion.api' +import type { GetPreOpinionAnswersParams, PreOpinionAnswersData } from '../preOpinion.types' import { preOpinionQueryKeys } from './preOpinionQueryKeys' /** @@ -18,9 +18,7 @@ import { preOpinionQueryKeys } from './preOpinionQueryKeys' * TanStack Query를 사용하여 약속의 사전 의견 목록을 조회합니다. * 멤버별 책 평가, 주제별 의견 등을 포함합니다. * - * @param params - 조회 파라미터 - * @param params.gatheringId - 모임 식별자 - * @param params.meetingId - 약속 식별자 + * @param params - 모임 ID와 약속 ID * * @returns TanStack Query 결과 객체 */ diff --git a/src/features/pre-opinion/index.ts b/src/features/pre-opinion/index.ts new file mode 100644 index 0000000..1486a3d --- /dev/null +++ b/src/features/pre-opinion/index.ts @@ -0,0 +1,16 @@ +// Components +export * from './components' + +// Hooks +export * from './hooks' + +// API +export * from './preOpinion.api' +export * from './preOpinion.endpoints' +export * from './preOpinion.mock' + +// Constants +export * from './preOpinion.constants' + +// Types +export * from './preOpinion.types' diff --git a/src/features/pre-opinion/preOpinion.api.ts b/src/features/pre-opinion/preOpinion.api.ts index f141310..0280580 100644 --- a/src/features/pre-opinion/preOpinion.api.ts +++ b/src/features/pre-opinion/preOpinion.api.ts @@ -5,10 +5,16 @@ import { api } from '@/api/client' import { PRE_OPINION_ENDPOINTS } from '@/features/pre-opinion/preOpinion.endpoints' -import { getMockPreOpinionDetail } from '@/features/pre-opinion/preOpinion.mock' +import { + getMockPreOpinionAnswers, + getMockPreOpinionDetail, +} from '@/features/pre-opinion/preOpinion.mock' import type { + DeleteMyPreOpinionAnswerParams, + GetPreOpinionAnswersParams, GetPreOpinionParams, GetPreOpinionResponse, + PreOpinionAnswersData, SavePreOpinionBody, SavePreOpinionParams, SubmitPreOpinionBody, @@ -53,6 +59,8 @@ export const savePreOpinion = async ( { gatheringId, meetingId, isFirstSave }: SavePreOpinionParams, body: SavePreOpinionBody ): Promise => { + if (USE_MOCK) return + if (isFirstSave) { return api.post(PRE_OPINION_ENDPOINTS.CREATE(gatheringId, meetingId), body) } @@ -74,5 +82,47 @@ export const submitPreOpinion = async ( meetingId: number, body: SubmitPreOpinionBody ): Promise => { + if (USE_MOCK) return + return api.patch(PRE_OPINION_ENDPOINTS.SUBMIT(gatheringId, meetingId), body) } + +/** + * 사전 의견 목록 조회 + * + * @description + * 약속의 사전 의견 목록(멤버별 책 평가 + 주제 의견)을 조회합니다. + * + * @param params - 모임 ID와 약속 ID + * + * @returns 사전 의견 목록 데이터 (topics + members) + */ +export const getPreOpinionAnswers = async ( + params: GetPreOpinionAnswersParams +): Promise => { + const { gatheringId, meetingId } = params + + if (USE_MOCK) { + await new Promise((resolve) => setTimeout(resolve, 500)) + return getMockPreOpinionAnswers() + } + + return api.get(PRE_OPINION_ENDPOINTS.ANSWERS(gatheringId, meetingId)) +} + +/** + * 내 사전 의견 삭제 + * + * @description + * 현재 로그인한 사용자의 사전 의견을 삭제합니다. + * + * @param params - 모임 ID와 약속 ID + */ +export const deleteMyPreOpinionAnswer = async ( + params: DeleteMyPreOpinionAnswerParams +): Promise => { + if (USE_MOCK) return + + const { gatheringId, meetingId } = params + return api.delete(PRE_OPINION_ENDPOINTS.DELETE_MY_ANSWER(gatheringId, meetingId)) +} diff --git a/src/features/preOpinions/preOpinions.constants.ts b/src/features/pre-opinion/preOpinion.constants.ts similarity index 80% rename from src/features/preOpinions/preOpinions.constants.ts rename to src/features/pre-opinion/preOpinion.constants.ts index 1671b60..f7f89a6 100644 --- a/src/features/preOpinions/preOpinions.constants.ts +++ b/src/features/pre-opinion/preOpinion.constants.ts @@ -1,4 +1,4 @@ -import type { MemberRole } from './preOpinions.types' +import type { MemberRole } from './preOpinion.types' /** API MemberRole → Avatar variant 매핑 */ export const ROLE_TO_AVATAR_VARIANT: Record = { diff --git a/src/features/pre-opinion/preOpinion.endpoints.ts b/src/features/pre-opinion/preOpinion.endpoints.ts index 7f6b5af..3e41e02 100644 --- a/src/features/pre-opinion/preOpinion.endpoints.ts +++ b/src/features/pre-opinion/preOpinion.endpoints.ts @@ -1,6 +1,13 @@ import { API_PATHS } from '@/api' export const PRE_OPINION_ENDPOINTS = { + // 사전 의견 목록 조회 (GET /api/gatherings/{gatheringId}/meetings/{meetingId}/answers) + ANSWERS: (gatheringId: number, meetingId: number) => + `${API_PATHS.GATHERINGS}/${gatheringId}/meetings/${meetingId}/answers`, + + // 내 사전 의견 삭제 (DELETE /api/gatherings/{gatheringId}/meetings/{meetingId}/topics/answers/me) + DELETE_MY_ANSWER: (gatheringId: number, meetingId: number) => + `${API_PATHS.GATHERINGS}/${gatheringId}/meetings/${meetingId}/topics/answers/me`, // 사전 의견 조회 (GET /api/gatherings/{gatheringId}/meetings/{meetingId}/answers/me) DETAIL: (gatheringId: number, meetingId: number) => `${API_PATHS.GATHERINGS}/${gatheringId}/meetings/${meetingId}/answers/me`, diff --git a/src/features/pre-opinion/preOpinion.mock.ts b/src/features/pre-opinion/preOpinion.mock.ts index 7be34e6..3ac9347 100644 --- a/src/features/pre-opinion/preOpinion.mock.ts +++ b/src/features/pre-opinion/preOpinion.mock.ts @@ -3,7 +3,89 @@ * @description 사전 의견 API 목데이터 */ -import type { GetPreOpinionResponse } from '@/features/pre-opinion/preOpinion.types' +import type { + GetPreOpinionResponse, + PreOpinionAnswersData, +} from '@/features/pre-opinion/preOpinion.types' + +/** + * 사전 의견 목록 목데이터 + */ +const mockPreOpinionAnswers: PreOpinionAnswersData = { + topics: [ + { + topicId: 1, + title: '책의 주요 메시지', + description: '이 책에서 전달하고자 하는 핵심 메시지는 무엇인가요?', + topicType: 'DISCUSSION', + topicTypeLabel: '토론형', + confirmOrder: 1, + }, + { + topicId: 2, + title: '가장 인상 깊었던 장면', + description: '책을 읽으며 가장 기억에 남았던 장면은 무엇인가요?', + topicType: 'DISCUSSION', + topicTypeLabel: '토론형', + confirmOrder: 2, + }, + ], + members: [ + { + memberInfo: { + userId: 1, + nickname: '독서왕', + profileImage: 'https://picsum.photos/seed/user1/100/100', + role: 'GATHERING_LEADER', + }, + isSubmitted: true, + bookReview: { + rating: 4.5, + keywordInfo: [ + { id: 3, name: '성장', type: 'BOOK' }, + { id: 7, name: '여운이 남는', type: 'IMPRESSION' }, + ], + }, + topicOpinions: [ + { topicId: 1, content: '이 책의 핵심 메시지는 자기 성찰이라고 생각합니다.' }, + { topicId: 2, content: '주인공이 선택의 기로에 서는 장면이 가장 인상 깊었습니다.' }, + ], + }, + { + memberInfo: { + userId: 10, + nickname: '밤독서', + profileImage: 'https://picsum.photos/seed/user3/100/100', + role: 'MEETING_LEADER', + }, + isSubmitted: true, + bookReview: { + rating: 3.0, + keywordInfo: [ + { id: 5, name: '관계', type: 'BOOK' }, + { id: 7, name: '여운이 남는', type: 'IMPRESSION' }, + ], + }, + topicOpinions: [ + { topicId: 1, content: null }, + { topicId: 2, content: '잔잔하지만 오래 남는 장면들이 많았습니다.' }, + ], + }, + { + memberInfo: { + userId: 2, + nickname: '페이지러버', + profileImage: 'https://picsum.photos/seed/user2/100/100', + role: 'MEMBER', + }, + isSubmitted: false, + bookReview: null, + topicOpinions: [], + }, + ], +} + +export const getMockPreOpinionAnswers = (): PreOpinionAnswersData => mockPreOpinionAnswers /** * 사전 의견 조회 목데이터 diff --git a/src/features/pre-opinion/preOpinion.types.ts b/src/features/pre-opinion/preOpinion.types.ts index 598737c..b12d621 100644 --- a/src/features/pre-opinion/preOpinion.types.ts +++ b/src/features/pre-opinion/preOpinion.types.ts @@ -4,9 +4,80 @@ */ import type { ReviewKeyword } from '@/features/book/book.types' +import type { KeywordType } from '@/features/keywords/keywords.types' import type { TopicType } from '../topics' +// ─── 사전 의견 목록 조회 관련 타입 ────────────────────────────────────────────── + +/** 멤버 역할 타입 */ +export type MemberRole = 'GATHERING_LEADER' | 'MEETING_LEADER' | 'MEMBER' + +/** 사전 의견 키워드 */ +export type PreOpinionKeyword = { + id: number + name: string + type: KeywordType +} + +/** 책 평가 정보 */ +export type BookReviewSummary = { + rating: number + keywordInfo: PreOpinionKeyword[] +} + +/** 멤버 정보 */ +export type PreOpinionMemberInfo = { + userId: number + nickname: string + profileImage: string + role: MemberRole +} + +/** 주제별 의견 */ +export type TopicOpinion = { + topicId: number + content: string | null +} + +/** 확정된 주제 항목 (목록 조회용) */ +export type PreOpinionAnswerTopic = { + topicId: number + title: string + description: string + topicType: TopicType + topicTypeLabel: string + confirmOrder: number +} + +/** 멤버별 사전 의견 */ +export type PreOpinionMember = { + memberInfo: PreOpinionMemberInfo + isSubmitted: boolean + bookReview: BookReviewSummary | null + topicOpinions: TopicOpinion[] +} + +/** 사전 의견 목록 조회 응답 데이터 */ +export type PreOpinionAnswersData = { + topics: PreOpinionAnswerTopic[] + members: PreOpinionMember[] +} + +/** 사전 의견 목록 조회 파라미터 */ +export type GetPreOpinionAnswersParams = { + gatheringId: number + meetingId: number +} + +/** 내 사전 의견 삭제 파라미터 */ +export type DeleteMyPreOpinionAnswerParams = { + gatheringId: number + meetingId: number +} + +// ─── 사전 의견 작성/조회 관련 타입 ────────────────────────────────────────────── + /** * 사전 의견 주제 항목 */ diff --git a/src/features/preOpinions/components/index.ts b/src/features/preOpinions/components/index.ts deleted file mode 100644 index 317611c..0000000 --- a/src/features/preOpinions/components/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './PreOpinionDetail' -export * from './PreOpinionMemberList' diff --git a/src/features/preOpinions/hooks/index.ts b/src/features/preOpinions/hooks/index.ts deleted file mode 100644 index 305c6e7..0000000 --- a/src/features/preOpinions/hooks/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export * from './preOpinionQueryKeys' -export * from './useDeleteMyPreOpinionAnswer' -export * from './usePreOpinionAnswers' diff --git a/src/features/preOpinions/hooks/preOpinionQueryKeys.ts b/src/features/preOpinions/hooks/preOpinionQueryKeys.ts deleted file mode 100644 index d4d25d6..0000000 --- a/src/features/preOpinions/hooks/preOpinionQueryKeys.ts +++ /dev/null @@ -1,21 +0,0 @@ -/** - * @file preOpinionQueryKeys.ts - * @description 사전 의견 관련 Query Key Factory - */ - -import type { GetPreOpinionAnswersParams } from '../preOpinions.types' - -/** - * Query Key Factory - * - * @description - * 사전 의견 관련 Query Key를 일관되게 관리하기 위한 팩토리 함수 - */ -export const preOpinionQueryKeys = { - all: ['preOpinions'] as const, - - // 사전 의견 목록 관련 - answers: () => [...preOpinionQueryKeys.all, 'answers'] as const, - answerList: (params: GetPreOpinionAnswersParams) => - [...preOpinionQueryKeys.answers(), params] as const, -} diff --git a/src/features/preOpinions/index.ts b/src/features/preOpinions/index.ts deleted file mode 100644 index e804ad9..0000000 --- a/src/features/preOpinions/index.ts +++ /dev/null @@ -1,13 +0,0 @@ -// Components -export * from './components' - -// Hooks -export * from './hooks' - -// API -export * from './preOpinions.api' -export * from './preOpinions.endpoints' -export * from './preOpinions.mock' - -// Types -export * from './preOpinions.types' diff --git a/src/features/preOpinions/preOpinions.api.ts b/src/features/preOpinions/preOpinions.api.ts deleted file mode 100644 index 158a104..0000000 --- a/src/features/preOpinions/preOpinions.api.ts +++ /dev/null @@ -1,59 +0,0 @@ -/** - * @file preOpinions.api.ts - * @description 사전 의견 API 요청 함수 - */ - -import { api } from '@/api/client' - -import { PRE_OPINIONS_ENDPOINTS } from './preOpinions.endpoints' -import { getMockPreOpinionAnswers } from './preOpinions.mock' -import type { - DeleteMyPreOpinionAnswerParams, - GetPreOpinionAnswersParams, - PreOpinionAnswersData, -} from './preOpinions.types' - -/** 목데이터 사용 여부 플래그 */ -const USE_MOCK = import.meta.env.VITE_USE_MOCK === 'true' - -/** - * 사전 의견 목록 조회 - * - * @description - * 약속의 사전 의견 목록(멤버별 책 평가 + 주제 의견)을 조회합니다. - * - * @param params - 조회 파라미터 - * @param params.gatheringId - 모임 식별자 - * @param params.meetingId - 약속 식별자 - * - * @returns 사전 의견 목록 데이터 (topics + members) - */ -export const getPreOpinionAnswers = async ( - params: GetPreOpinionAnswersParams -): Promise => { - const { gatheringId, meetingId } = params - - if (USE_MOCK) { - await new Promise((resolve) => setTimeout(resolve, 500)) - return getMockPreOpinionAnswers() - } - - return api.get(PRE_OPINIONS_ENDPOINTS.ANSWERS(gatheringId, meetingId)) -} - -/** - * 내 사전 의견 삭제 - * - * @description - * 현재 로그인한 사용자의 사전 의견을 삭제합니다. - * - * @param params - 삭제 파라미터 - * @param params.gatheringId - 모임 식별자 - * @param params.meetingId - 약속 식별자 - */ -export const deleteMyPreOpinionAnswer = async ( - params: DeleteMyPreOpinionAnswerParams -): Promise => { - const { gatheringId, meetingId } = params - return api.delete(PRE_OPINIONS_ENDPOINTS.DELETE_MY_ANSWER(gatheringId, meetingId)) -} diff --git a/src/features/preOpinions/preOpinions.endpoints.ts b/src/features/preOpinions/preOpinions.endpoints.ts deleted file mode 100644 index d4a748b..0000000 --- a/src/features/preOpinions/preOpinions.endpoints.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { API_PATHS } from '@/api' - -export const PRE_OPINIONS_ENDPOINTS = { - // 사전 의견 목록 조회 (GET /api/gatherings/{gatheringId}/meetings/{meetingId}/answers) - ANSWERS: (gatheringId: number, meetingId: number) => - `${API_PATHS.GATHERINGS}/${gatheringId}/meetings/${meetingId}/answers`, - - // 내 사전 의견 삭제 (DELETE /api/gatherings/{gatheringId}/meetings/{meetingId}/topics/answers/me) - DELETE_MY_ANSWER: (gatheringId: number, meetingId: number) => - `${API_PATHS.GATHERINGS}/${gatheringId}/meetings/${meetingId}/topics/answers/me`, -} as const diff --git a/src/features/preOpinions/preOpinions.mock.ts b/src/features/preOpinions/preOpinions.mock.ts deleted file mode 100644 index 3b7eef8..0000000 --- a/src/features/preOpinions/preOpinions.mock.ts +++ /dev/null @@ -1,102 +0,0 @@ -/** - * @file preOpinions.mock.ts - * @description 사전 의견 API 목데이터 - */ - -import type { PreOpinionAnswersData } from './preOpinions.types' - -/** - * 사전 의견 목록 목데이터 - */ -const mockPreOpinionAnswers: PreOpinionAnswersData = { - topics: [ - { - topicId: 1, - title: '책의 주요 메시지', - description: '이 책에서 전달하고자 하는 핵심 메시지는 무엇인가요?', - topicType: 'DISCUSSION', - topicTypeLabel: '토론형', - confirmOrder: 1, - }, - { - topicId: 2, - title: '가장 인상 깊었던 장면', - description: '책을 읽으며 가장 기억에 남았던 장면은 무엇인가요?', - topicType: 'DISCUSSION', - topicTypeLabel: '토론형', - confirmOrder: 2, - }, - ], - members: [ - { - memberInfo: { - userId: 1, - nickname: '독서왕', - profileImage: 'https://picsum.photos/seed/user1/100/100', - role: 'GATHERING_LEADER', - }, - isSubmitted: true, - bookReview: { - rating: 4.5, - keywordInfo: [ - { id: 3, name: '성장', type: 'BOOK' }, - { id: 7, name: '여운이 남는', type: 'IMPRESSION' }, - ], - }, - topicOpinions: [ - { - topicId: 1, - content: '이 책의 핵심 메시지는 자기 성찰이라고 생각합니다.', - }, - { - topicId: 2, - content: '주인공이 선택의 기로에 서는 장면이 가장 인상 깊었습니다.', - }, - ], - }, - { - memberInfo: { - userId: 10, - nickname: '밤독서', - profileImage: 'https://picsum.photos/seed/user3/100/100', - role: 'MEETING_LEADER', - }, - isSubmitted: true, - bookReview: { - rating: 3.0, - keywordInfo: [ - { id: 5, name: '관계', type: 'BOOK' }, - { id: 7, name: '여운이 남는', type: 'IMPRESSION' }, - ], - }, - topicOpinions: [ - { - topicId: 1, - content: null, - }, - { - topicId: 2, - content: '잔잔하지만 오래 남는 장면들이 많았습니다.', - }, - ], - }, - { - memberInfo: { - userId: 2, - nickname: '페이지러버', - profileImage: 'https://picsum.photos/seed/user2/100/100', - role: 'MEMBER', - }, - isSubmitted: false, - bookReview: null, - topicOpinions: [], - }, - ], -} - -/** - * 사전 의견 목록 목데이터 반환 함수 - */ -export const getMockPreOpinionAnswers = (): PreOpinionAnswersData => { - return mockPreOpinionAnswers -} diff --git a/src/features/preOpinions/preOpinions.types.ts b/src/features/preOpinions/preOpinions.types.ts deleted file mode 100644 index 8e5fbd2..0000000 --- a/src/features/preOpinions/preOpinions.types.ts +++ /dev/null @@ -1,73 +0,0 @@ -/** - * @file preOpinions.types.ts - * @description 사전 의견 API 관련 타입 정의 - */ - -import type { KeywordType } from '@/features/keywords/keywords.types' -import type { TopicType } from '@/features/topics/topics.types' - -/** 멤버 역할 타입 */ -export type MemberRole = 'GATHERING_LEADER' | 'MEETING_LEADER' | 'MEMBER' - -/** 사전 의견 키워드 */ -export type PreOpinionKeyword = { - id: number - name: string - type: KeywordType -} - -/** 책 평가 정보 */ -export type BookReviewSummary = { - rating: number - keywordInfo: PreOpinionKeyword[] -} - -/** 멤버 정보 */ -export type PreOpinionMemberInfo = { - userId: number - nickname: string - profileImage: string - role: MemberRole -} - -/** 주제별 의견 */ -export type TopicOpinion = { - topicId: number - content: string | null -} - -/** 확정된 주제 항목 */ -export type PreOpinionTopic = { - topicId: number - title: string - description: string - topicType: TopicType - topicTypeLabel: string - confirmOrder: number -} - -/** 멤버별 사전 의견 */ -export type PreOpinionMember = { - memberInfo: PreOpinionMemberInfo - isSubmitted: boolean - bookReview: BookReviewSummary | null - topicOpinions: TopicOpinion[] -} - -/** 사전 의견 목록 조회 응답 데이터 */ -export type PreOpinionAnswersData = { - topics: PreOpinionTopic[] - members: PreOpinionMember[] -} - -/** 사전 의견 목록 조회 파라미터 */ -export type GetPreOpinionAnswersParams = { - gatheringId: number - meetingId: number -} - -/** 내 사전 의견 삭제 파라미터 */ -export type DeleteMyPreOpinionAnswerParams = { - gatheringId: number - meetingId: number -} diff --git a/src/pages/PreOpinions/PreOpinionListPage.tsx b/src/pages/PreOpinions/PreOpinionListPage.tsx index 2e54739..4d18948 100644 --- a/src/pages/PreOpinions/PreOpinionListPage.tsx +++ b/src/pages/PreOpinions/PreOpinionListPage.tsx @@ -5,7 +5,7 @@ import { PreOpinionDetail, PreOpinionMemberList, usePreOpinionAnswers, -} from '@/features/preOpinions' +} from '@/features/pre-opinion' import SubPageHeader from '@/shared/components/SubPageHeader' import { Spinner } from '@/shared/ui' import { useGlobalModalStore } from '@/store' diff --git a/src/pages/PreOpinions/PreOpinionWritePage.tsx b/src/pages/PreOpinions/PreOpinionWritePage.tsx index d59d000..834be7b 100644 --- a/src/pages/PreOpinions/PreOpinionWritePage.tsx +++ b/src/pages/PreOpinions/PreOpinionWritePage.tsx @@ -145,7 +145,7 @@ export default function PreOpinionWritePage() { if (isLoading || !preOpinion) { return ( <> - +
@@ -155,7 +155,7 @@ export default function PreOpinionWritePage() { return ( <> - + -
+

diff --git a/src/shared/components/SubPageHeader.tsx b/src/shared/components/SubPageHeader.tsx index f850130..0db0a4a 100644 --- a/src/shared/components/SubPageHeader.tsx +++ b/src/shared/components/SubPageHeader.tsx @@ -12,6 +12,8 @@ export interface SubPageHeaderProps { to?: string /** 외부에서 전달하는 추가 클래스 */ className?: string + /** 스크롤 시 하단 shadow 비활성화 */ + disableShadow?: boolean } /** @@ -31,7 +33,12 @@ export interface SubPageHeaderProps { * * ``` */ -export default function SubPageHeader({ label = '뒤로가기', to, className }: SubPageHeaderProps) { +export default function SubPageHeader({ + label = '뒤로가기', + to, + className, + disableShadow = false, +}: SubPageHeaderProps) { const navigate = useNavigate() const isScrolled = useScrollShadow() @@ -48,11 +55,11 @@ export default function SubPageHeader({ label = '뒤로가기', to, className }: aria-label={`${label} 페이지로 이동`} className={cn( 'sticky top-gnb-height z-40 bg-white transition-shadow', - isScrolled && 'shadow-drop-bottom', + isScrolled && !disableShadow && 'shadow-drop-bottom', className )} > -

+
{label}