Skip to content

feat: 티켓 옵션 페이지 연동#154

Merged
xaexunxang merged 14 commits intodevelopfrom
feat/#132/ticket-option-api
May 30, 2025
Merged

feat: 티켓 옵션 페이지 연동#154
xaexunxang merged 14 commits intodevelopfrom
feat/#132/ticket-option-api

Conversation

@xaexunxang
Copy link
Copy Markdown
Contributor

@xaexunxang xaexunxang commented May 30, 2025

티켓 옵션 및 부착된 티켓 옵션 조회

스크린샷 2025-05-30 17 36 35 - 생성한 이벤트(eventId) + 티켓(ticketId) + 티켓 옵션(ticketOptionId) 를 기준으로 api 연동

티켓 옵션 생성

스크린샷 2025-05-30 17 39 17 image

티켓 옵션 상세 조회 및 수정

스크린샷 2025-05-30 17 41 07 스크린샷 2025-05-30 17 42 28

티켓 옵션 삭제

스크린샷 2025-05-30 17 43 27 image

티켓 옵션 부착 및 부착 취소

스크린샷 2025-05-30 17 46 09 스크린샷 2025-05-30 17 45 49

기타 사항

  1. 현재 생성, 수정, 삭제, 부착, 부착 취소 등 리패칭(Refetching)으로 구현되어있음 → 서버 요청이 매번 이루어짐, 그래서 낙관적인 UI 로 변경해야하는지. 왜냐하면 새로고침했을 때 이벤트, 티켓, 티켓 옵션에 대한 데이터들이 반영되기 때문.
  2. 티켓에 추가 옵션 부착 하기 페이지에서 옵션 영역이 아닌 티켓 영역 UI에 고민이 생김. 만약에 티켓이 3개가 만들어져있고, 하나만 티켓 옵션이 부착되어있지 않았을 때의 UI에서 살짝 이질감이 느껴짐

Summary by CodeRabbit

  • 신규 기능

    • 티켓 옵션 생성, 수정, 삭제, 조회, 티켓에 옵션 연결 및 해제 기능이 추가되었습니다.
    • 티켓 옵션 폼과 옵션 리스트 섹션 컴포넌트 도입으로 옵션 생성 및 편집 UI가 개선되었습니다.
    • 드래그 앤 드롭을 통한 티켓 옵션과 티켓 간 옵션 이동 및 연결/해제 기능이 도입되었습니다.
    • 티켓 옵션 상세 페이지와 옵션 편집을 위한 라우트가 추가되었습니다.
  • 버그 수정 및 리팩터링

    • 티켓 옵션 관련 페이지와 컴포넌트가 서버 데이터 기반으로 리팩터링되어 상태 관리가 간소화되고 일관성이 향상되었습니다.
    • 일부 페이지에서 ReactQueryDevtools가 개발 환경에서만 활성화되도록 변경되고, 불필요한 개발자 도구 사용이 제거되었습니다.
    • 레이아웃 및 컴포넌트 구조가 더 명확하고 깔끔하게 개선되었습니다.
  • 문서 및 스타일

    • 타입 선언과 코드 스타일이 정비되어 유지보수성이 향상되었습니다.

@xaexunxang xaexunxang self-assigned this May 30, 2025
@xaexunxang xaexunxang added the 🔧 Feature 기능 구현 label May 30, 2025
@xaexunxang xaexunxang linked an issue May 30, 2025 that may be closed by this pull request
5 tasks
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented May 30, 2025

Caution

Review failed

The pull request is closed.

"""

Walkthrough

티켓 옵션 생성, 수정, 드래그 앤 드롭 기능을 전면적으로 리팩터링하고 확장하는 대규모 변경입니다. 새로운 API 클라이언트, 커스텀 훅, 리듀서, 모델, UI 컴포넌트가 추가되었으며, 기존 페이지와 컴포넌트는 서버 기반 데이터와 중앙 집중식 폼 로직을 사용하도록 전환되었습니다. 라우트 및 라우터 설정도 이에 맞게 확장되었습니다.

Changes

파일/그룹 변경 요약
src/app/Layout.tsx JSX 구조 정리 및 중첩 div 정리, 불필요한 태그 제거
src/app/routes/Router.tsx, src/app/routes/routes.ts 티켓 옵션 수정 페이지 라우트 추가 및 경로 상수 정의
src/features/dashboard/ui/DragArea.tsx, src/features/dashboard/ui/DraggableList.tsx 데이터 구조 단순화, props 구조 변경, 내부 mutation 훅 도입, 옵션 삭제/분리 로직 변경, 라우트 파라미터 및 네비게이션 활용, 액션 버튼 조건부 렌더링
src/features/event/ui/EventList.tsx, src/pages/home/ui/MainPage.tsx ReactQueryDevtools import 및 사용 제거
src/main.tsx ReactQueryDevtools import 및 QueryClientProvider 내에 devtools 추가
src/features/ticket/api/ticketOption.ts 티켓 옵션 CRUD 및 연결/분리 API 클라이언트 신설
src/features/ticket/hooks/useTicketOptionDnD.ts 티켓 옵션 드래그 앤 드롭 커스텀 훅 추가, attach/detach mutation 연동
src/features/ticket/hooks/useTicketOptionForm.ts 티켓 옵션 폼 관리용 커스텀 훅 추가, 상태 및 핸들러 통합
src/features/ticket/hooks/useTicketOptionHook.ts 티켓 옵션 관련 CRUD 및 연결/분리용 React Query 훅 다수 추가
src/features/ticket/model/ticketOption.ts, src/features/ticket/model/ticketOptionReducer.ts 티켓 옵션 상태/액션/리듀서 및 타입 정의 신설
src/features/ticket/ui/TicketOptionFormSection.tsx, src/features/ticket/ui/TicketOptionListSection.tsx 티켓 옵션 폼 및 옵션 리스트 UI 컴포넌트 신설
src/pages/dashboard/ui/ticket/TicketOptionCreatePage.tsx 기존 로컬 상태 및 핸들러 제거, 폼 훅 및 UI 컴포넌트로 대체, 데이터 fetch 및 초기화 로직 정비
src/pages/dashboard/ui/ticket/TicketOptionPage.tsx 로컬 옵션/드래그 영역 상태 제거, 서버 데이터 기반 렌더링 및 DnD 훅 연동, 티켓별 옵션 표시 및 모드 토글 추가
tsconfig.app.json 배열 항목 멀티라인 포맷팅 등 JSON 포맷 정리 (기능 변화 없음)

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant TicketOptionPage
    participant useTicketOptionDnD
    participant API
    User->>TicketOptionPage: 옵션을 드래그 앤 드롭
    TicketOptionPage->>useTicketOptionDnD: onDragEnd 호출
    useTicketOptionDnD->>API: attach/detachTicketOption API 호출
    API-->>useTicketOptionDnD: 응답 반환
    useTicketOptionDnD-->>TicketOptionPage: 상태 갱신 트리거
    TicketOptionPage-->>User: UI 갱신
Loading
sequenceDiagram
    participant User
    participant TicketOptionCreatePage
    participant useTicketOptionForm
    participant useTicketOptionHook
    participant API
    User->>TicketOptionCreatePage: 폼 입력 및 저장 클릭
    TicketOptionCreatePage->>useTicketOptionForm: handleSave 호출
    useTicketOptionForm->>useTicketOptionHook: create/modifyTicketOptionMutation 실행
    useTicketOptionHook->>API: create/modifyTicketOption API 호출
    API-->>useTicketOptionHook: 응답 반환
    useTicketOptionHook-->>useTicketOptionForm: 성공/실패 처리
    useTicketOptionForm-->>TicketOptionCreatePage: 결과 반영
    TicketOptionCreatePage-->>User: 네비게이션/알림 등 피드백
Loading

Possibly related PRs

  • GoTogether-Inc/frontend#79: 티켓 옵션 생성 및 드래그 앤 드롭 UI의 초기 도입과 관련된 PR로, 이번 PR은 해당 기능을 대대적으로 리팩터링하고 확장합니다.

Suggested reviewers

  • hyeeuncho

Poem

🐇
티켓 옵션 춤추듯, 드래그 앤 드롭
폼 훅으로 깔끔하게, 서버와 톡톡!
옵션 붙이고 떼고, 새로 만든 길
코드도 정리하고, UI는 더 빛나길
토끼는 말해요, "이제 진짜 멋져요!"
🥕✨
"""


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8cf78b3 and b510ece.

📒 Files selected for processing (19)
  • src/app/Layout.tsx (1 hunks)
  • src/app/routes/Router.tsx (1 hunks)
  • src/app/routes/routes.ts (1 hunks)
  • src/features/dashboard/ui/DragArea.tsx (2 hunks)
  • src/features/dashboard/ui/DraggableList.tsx (2 hunks)
  • src/features/event/ui/EventList.tsx (0 hunks)
  • src/features/ticket/api/ticketOption.ts (1 hunks)
  • src/features/ticket/hooks/useTicketOptionDnD.ts (1 hunks)
  • src/features/ticket/hooks/useTicketOptionForm.ts (1 hunks)
  • src/features/ticket/hooks/useTicketOptionHook.ts (1 hunks)
  • src/features/ticket/model/ticketOption.ts (1 hunks)
  • src/features/ticket/model/ticketOptionReducer.ts (1 hunks)
  • src/features/ticket/ui/TicketOptionFormSection.tsx (1 hunks)
  • src/features/ticket/ui/TicketOptionListSection.tsx (1 hunks)
  • src/main.tsx (2 hunks)
  • src/pages/dashboard/ui/ticket/TicketOptionCreatePage.tsx (1 hunks)
  • src/pages/dashboard/ui/ticket/TicketOptionPage.tsx (2 hunks)
  • src/pages/home/ui/MainPage.tsx (0 hunks)
  • tsconfig.app.json (1 hunks)
✨ Finishing Touches
  • 📝 Generate Docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 19

🧹 Nitpick comments (15)
src/main.tsx (1)

6-6: ReactQueryDevtools import: 개발용 의존성 분리 및 조건부 로딩 고려
ReactQueryDevtools는 개발 환경에서만 사용되는 도구이므로, package.jsondevDependencies로 이동하고 프로덕션 번들에 포함되지 않도록 동적 import 혹은 process.env.NODE_ENV === 'development' 조건부 로딩을 적용하는 것을 권장합니다.

프로덕션 빌드에 devtools가 포함되지 않았는지 확인해주세요.

src/app/routes/routes.ts (1)

45-45: 라우트 경로의 의미적 일관성 개선 권장

편집 기능임에도 불구하고 경로에 'create'가 포함되어 있어 혼란을 줄 수 있습니다. 일반적으로 편집 기능은 /edit/:id 패턴을 사용합니다.

다음과 같이 변경하는 것을 고려해보세요:

-  ticketOptionEdit: `${MAIN_ROUTES.dashboard}/ticket/option/create/:optionId`,
+  ticketOptionEdit: `${MAIN_ROUTES.dashboard}/ticket/option/edit/:optionId`,

이렇게 하면 URL만 보고도 생성과 편집을 명확히 구분할 수 있습니다.

src/features/ticket/ui/TicketOptionListSection.tsx (2)

22-42: React key로 index 사용 시 주의사항

옵션을 추가/삭제할 때 React의 재조정(reconciliation) 과정에서 문제가 발생할 수 있습니다. 특히 입력 필드의 포커스가 유지되지 않을 수 있습니다.

각 옵션에 고유 ID를 부여하는 것을 고려해보세요:

-{getActiveOptions().options.map((option, index) => (
-  <React.Fragment key={index}>
+{getActiveOptions().options.map((option, index) => (
+  <React.Fragment key={`option-${state.question.responseFormat}-${index}`}>

또는 옵션 데이터 구조에 고유 ID를 추가하는 것이 더 좋은 해결책일 수 있습니다.


55-55: 빈 div 렌더링에 대한 설명 추가 권장

'자유로운 텍스트' 형식일 때 빈 div가 렌더링되는 것이 의도적인지 명확하지 않습니다.

주석을 추가하거나 안내 메시지를 표시하는 것을 고려해보세요:

-{state.question.responseFormat === '자유로운 텍스트' && <div></div>}
+{state.question.responseFormat === '자유로운 텍스트' && (
+  <div className="text-gray-500 text-sm">
+    자유로운 텍스트 응답은 별도의 옵션이 필요하지 않습니다.
+  </div>
+)}
src/features/ticket/ui/TicketOptionFormSection.tsx (1)

34-34: Nullish coalescing 연산자 주변 공백 일관성

코드 스타일 일관성을 위해 공백을 추가하세요.

-value={state.question.description?? ''}
+value={state.question.description ?? ''}
src/features/ticket/hooks/useTicketOptionForm.ts (1)

218-218: 일관된 알림 시스템 사용 필요

다른 곳에서는 커스텀 alert를 사용하는데 여기서는 window.alert를 사용합니다.

일관성을 위해 프로젝트에서 사용하는 알림 시스템(토스트, 모달 등)을 사용하는 것을 권장합니다.

src/pages/dashboard/ui/ticket/TicketOptionPage.tsx (3)

80-80: React children 전달 방식 개선

children을 prop으로 전달하는 대신 JSX 요소로 전달하세요.

-<IconText iconPath={<img src={Option} alt="추가 버튼" />} children="옵션" className="font-bold pl-2" />
+<IconText iconPath={<img src={Option} alt="추가 버튼" />} className="font-bold pl-2">
+  옵션
+</IconText>

113-113: React children 전달 방식 개선

children을 prop으로 전달하는 대신 JSX 요소로 전달하세요.

-<IconText iconPath={<img src={Ticket} alt="추가 버튼" />} children="티켓" className="font-bold pl-2" />
+<IconText iconPath={<img src={Ticket} alt="추가 버튼" />} className="font-bold pl-2">
+  티켓
+</IconText>

22-27: 중복 코드 리팩토링

배열 처리 로직이 중복되어 있습니다. 유틸리티 함수로 추출하여 재사용성을 높이세요.

별도의 유틸리티 함수 파일에 추가:

// utils/arrayHelpers.ts
export const normalizeToArray = <T>(data: T | T[] | undefined): T[] => {
  if (!data) return [];
  return Array.isArray(data) ? data : [data];
};

그리고 해당 부분을 다음과 같이 수정:

-  const tickets = Array.isArray(ticketsData?.result)
-    ? ticketsData.result
-    : ticketsData?.result
-    ? [ticketsData.result]
-    : [];
+  const tickets = normalizeToArray(ticketsData?.result);

-  const options = Array.isArray(ticketOptionsData?.result)
-    ? ticketOptionsData.result
-    : ticketOptionsData?.result
-    ? [ticketOptionsData.result]
-    : [];
+  const options = normalizeToArray(ticketOptionsData?.result);

Also applies to: 62-67

src/pages/dashboard/ui/ticket/TicketOptionCreatePage.tsx (1)

30-31: 편집 모드에 따른 UI 차별화

편집 모드와 생성 모드에 따라 제목과 설명을 다르게 표시하세요.

-        <div className="text-center text-xl font-bold mb-5">티켓 옵션 생성</div>
-        <p className="text-gray-400 text-sm mb-5">티켓 옵션을 생성할 수 있습니다.</p>
+        <div className="text-center text-xl font-bold mb-5">
+          티켓 옵션 {isEditing ? '수정' : '생성'}
+        </div>
+        <p className="text-gray-400 text-sm mb-5">
+          티켓 옵션을 {isEditing ? '수정' : '생성'}할 수 있습니다.
+        </p>
src/features/ticket/model/ticketOptionReducer.ts (2)

24-24: 주석 개선 필요

더 명확한 주석으로 코드의 의도를 전달하세요.

-// 각 케이스는 동작하는 함수 정의. 세부 작동은 dispatch
+// 티켓 옵션 상태를 관리하는 리듀서 함수

104-129: 매직 넘버 제거

하드코딩된 숫자 3을 상수로 추출하여 유지보수성을 향상시키세요.

파일 상단에 상수 추가:

const DEFAULT_OPTIONS_COUNT = 3;

그리고 코드에서 사용:

   singleOptions: {
-    options: Array(3).fill(''),
+    options: Array(DEFAULT_OPTIONS_COUNT).fill(''),
   },
   multiOptions: {
-    options: Array(3).fill(''),
+    options: Array(DEFAULT_OPTIONS_COUNT).fill(''),
   },
src/features/dashboard/ui/DraggableList.tsx (1)

36-42: 표시 포맷 헬퍼 함수가 유용합니다.

영어 코드를 한국어로 변환하는 함수가 사용자 경험을 향상시킵니다. 다만 타입 안전성을 위해 개선할 수 있습니다.

-const getDisplayFormat = (format: string) => {
+const getDisplayFormat = (format: 'SINGLE' | 'MULTIPLE' | 'TEXT') => {
  if (format === 'SINGLE') return '객관식';
  if (format === 'MULTIPLE') return '여러개 선택';
  if (format === 'TEXT') return '자유로운 텍스트';
-  
-  return format;
+  return format; // 이 줄은 타입상 도달할 수 없음
};
src/features/ticket/hooks/useTicketOptionHook.ts (2)

54-70: 삭제 성공 시 피드백 방식을 통일해주세요.

다른 mutation들과 달리 삭제 성공 시에만 console.log를 사용하고 있습니다. 일관성을 위해 alert를 사용하는 것이 좋겠습니다.

onSuccess: (_, ticketOptionId) => {
  // 티켓 옵션 목록과 상세 정보 쿼리 리패칭
  queryClient.invalidateQueries({ queryKey: ['ticketOptions', id] });
  queryClient.invalidateQueries({ queryKey: ['ticketOptionDetail', ticketOptionId] });
- console.log('티켓 옵션이 성공적으로 삭제되었습니다.');
+ alert('티켓 옵션이 성공적으로 삭제되었습니다.');
},

103-134: 부착/부착취소 훅의 에러 처리 방식을 개선해주세요.

부착과 부착취소 훅에서 에러 발생 시 console.log를 사용하고 있는데, 사용자에게 더 명확한 피드백을 위해 alert를 사용하는 것이 좋겠습니다.

// useAttachTicketOptionMutation의 onError
onError: () => {
- console.log('티켓 옵션 부착에 실패했습니다. 다시 시도해주세요.');
+ alert('티켓 옵션 부착에 실패했습니다. 다시 시도해주세요.');
},

// useDetachTicketOptionMutation의 onError  
onError: () => {
- console.log('티켓에 부착된 티켓 옵션 부착 취소에 실패했습니다. 다시 시도해주세요.');
+ alert('티켓에 부착된 티켓 옵션 부착 취소에 실패했습니다. 다시 시도해주세요.');
},
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c0dfb1d and 1d6b1a3.

📒 Files selected for processing (19)
  • src/app/Layout.tsx (1 hunks)
  • src/app/routes/Router.tsx (1 hunks)
  • src/app/routes/routes.ts (1 hunks)
  • src/features/dashboard/ui/DragArea.tsx (2 hunks)
  • src/features/dashboard/ui/DraggableList.tsx (3 hunks)
  • src/features/event/ui/EventList.tsx (0 hunks)
  • src/features/ticket/api/ticketOption.ts (1 hunks)
  • src/features/ticket/hooks/useTicketOptionDnD.ts (1 hunks)
  • src/features/ticket/hooks/useTicketOptionForm.ts (1 hunks)
  • src/features/ticket/hooks/useTicketOptionHook.ts (1 hunks)
  • src/features/ticket/model/ticketOption.ts (1 hunks)
  • src/features/ticket/model/ticketOptionReducer.ts (1 hunks)
  • src/features/ticket/ui/TicketOptionFormSection.tsx (1 hunks)
  • src/features/ticket/ui/TicketOptionListSection.tsx (1 hunks)
  • src/main.tsx (2 hunks)
  • src/pages/dashboard/ui/ticket/TicketOptionCreatePage.tsx (1 hunks)
  • src/pages/dashboard/ui/ticket/TicketOptionPage.tsx (2 hunks)
  • src/pages/home/ui/MainPage.tsx (0 hunks)
  • tsconfig.app.json (1 hunks)
💤 Files with no reviewable changes (2)
  • src/features/event/ui/EventList.tsx
  • src/pages/home/ui/MainPage.tsx
🧰 Additional context used
🧬 Code Graph Analysis (9)
src/app/routes/Router.tsx (1)
src/app/routes/routes.ts (1)
  • DASHBOARD_ROUTES (36-51)
src/features/ticket/hooks/useTicketOptionDnD.ts (1)
src/features/ticket/hooks/useTicketOptionHook.ts (2)
  • useAttachTicketOptionMutation (103-117)
  • useDetachTicketOptionMutation (120-134)
src/features/ticket/hooks/useTicketOptionForm.ts (3)
src/features/ticket/model/ticketOption.ts (3)
  • State (4-22)
  • Action (25-35)
  • TicketOptionsType (46-56)
src/features/ticket/model/ticketOptionReducer.ts (4)
  • State (135-135)
  • Action (135-135)
  • ticketOptionReducer (25-133)
  • initialState (4-22)
src/features/ticket/hooks/useTicketOptionHook.ts (3)
  • useCreateTicketOptionMutation (16-30)
  • useModifyTicketOptionMutation (33-51)
  • useGetTicketOptionDetail (94-100)
src/features/ticket/model/ticketOptionReducer.ts (1)
src/features/ticket/model/ticketOption.ts (2)
  • State (4-22)
  • Action (25-35)
src/features/dashboard/ui/DragArea.tsx (2)
src/features/ticket/model/ticketOption.ts (1)
  • TicketOptionsType (46-56)
design-system/ui/buttons/HorizontalCardButton.tsx (1)
  • HorizontalCardButton (12-40)
src/features/ticket/api/ticketOption.ts (3)
src/features/ticket/model/ticketOption.ts (2)
  • TicketOptionRequest (37-44)
  • TicketOptionResponse (58-58)
src/shared/types/api/http-client.ts (1)
  • axiosClient (6-13)
src/shared/types/api/apiResponse.ts (1)
  • ApiResponse (1-5)
src/features/ticket/hooks/useTicketOptionHook.ts (2)
src/features/ticket/model/ticketOption.ts (2)
  • TicketOptionRequest (37-44)
  • TicketOptionResponse (58-58)
src/features/ticket/api/ticketOption.ts (8)
  • createTicketOption (6-9)
  • modifyTicketOption (12-18)
  • deleteTicketOption (21-24)
  • getTicketOptions (27-30)
  • getAttachedTicketOptions (33-36)
  • getTicketOptionDetail (39-42)
  • attachTicketOption (45-54)
  • detachTicketOption (57-68)
src/features/dashboard/ui/DraggableList.tsx (1)
src/features/ticket/hooks/useTicketOptionHook.ts (2)
  • useDetachTicketOptionMutation (120-134)
  • useDeleteTicketOptionMutation (54-70)
src/features/ticket/model/ticketOption.ts (2)
src/features/ticket/model/ticketOptionReducer.ts (2)
  • State (135-135)
  • Action (135-135)
src/shared/types/api/apiResponse.ts (1)
  • ApiResponse (1-5)
🪛 Biome (1.9.4)
tsconfig.app.json

[error] 13-13: JSON standard does not allow comments.

(parse)


[error] 14-14: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 14-14: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 14-14: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 14-14: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 15-15: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 15-15: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 15-15: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 15-15: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 16-16: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 16-16: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 16-16: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 16-16: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 17-17: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 17-17: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 17-17: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 17-17: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 18-18: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 18-18: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 18-18: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 18-18: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 19-19: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 19-19: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 19-19: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 19-20: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 20-20: JSON standard does not allow comments.

(parse)


[error] 21-21: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 21-21: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 21-21: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 21-21: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 22-22: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 22-22: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 22-22: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 22-22: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 23-23: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 23-23: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 23-23: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 23-23: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 24-24: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 24-24: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 24-24: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 24-24: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 25-25: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 25-25: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 25-25: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 25-25: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 26-26: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 26-26: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 26-28: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 29-29: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 30-30: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 30-30: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 30-33: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 33-34: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)

src/pages/dashboard/ui/ticket/TicketOptionPage.tsx

[error] 48-48: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


[error] 84-84: Avoid passing children using a prop

The canonical way to pass children in React is to use JSX elements

(lint/correctness/noChildrenProp)


[error] 117-118: Avoid passing children using a prop

The canonical way to pass children in React is to use JSX elements

(lint/correctness/noChildrenProp)

src/features/ticket/api/ticketOption.ts

[error] 22-22: Don't use '{}' as a type.

Prefer explicitly define the object shape. '{}' means "any non-nullable value".

(lint/complexity/noBannedTypes)


[error] 23-23: Don't use '{}' as a type.

Prefer explicitly define the object shape. '{}' means "any non-nullable value".

(lint/complexity/noBannedTypes)


[error] 67-67: Don't use '{}' as a type.

Prefer explicitly define the object shape. '{}' means "any non-nullable value".

(lint/complexity/noBannedTypes)

🪛 ESLint
src/pages/dashboard/ui/ticket/TicketOptionPage.tsx

[error] 53-53: Unexpected any. Specify a different type.

(@typescript-eslint/no-explicit-any)

src/features/ticket/api/ticketOption.ts

[error] 21-21: The {} ("empty object") type allows any non-nullish value, including literals like 0 and "".

  • If that's what you want, disable this lint rule with an inline comment or configure the 'allowObjectTypes' rule option.
  • If you want a type meaning "any object", you probably want object instead.
  • If you want a type meaning "any value", you probably want unknown instead.

(@typescript-eslint/no-empty-object-type)


[error] 22-22: The {} ("empty object") type allows any non-nullish value, including literals like 0 and "".

  • If that's what you want, disable this lint rule with an inline comment or configure the 'allowObjectTypes' rule option.
  • If you want a type meaning "any object", you probably want object instead.
  • If you want a type meaning "any value", you probably want unknown instead.

(@typescript-eslint/no-empty-object-type)


[error] 60-60: The {} ("empty object") type allows any non-nullish value, including literals like 0 and "".

  • If that's what you want, disable this lint rule with an inline comment or configure the 'allowObjectTypes' rule option.
  • If you want a type meaning "any object", you probably want object instead.
  • If you want a type meaning "any value", you probably want unknown instead.

(@typescript-eslint/no-empty-object-type)


[error] 61-61: The {} ("empty object") type allows any non-nullish value, including literals like 0 and "".

  • If that's what you want, disable this lint rule with an inline comment or configure the 'allowObjectTypes' rule option.
  • If you want a type meaning "any object", you probably want object instead.
  • If you want a type meaning "any value", you probably want unknown instead.

(@typescript-eslint/no-empty-object-type)

src/features/ticket/model/ticketOption.ts

[error] 58-58: An interface declaring no members is equivalent to its supertype.

(@typescript-eslint/no-empty-object-type)

🔇 Additional comments (33)
tsconfig.app.json (3)

6-10: lib 배열 멀티라인 포맷팅 적용
기능에는 변화가 없으며, 요소별로 줄을 나눠 가독성을 높였습니다.


26-28: types 배열 멀티라인 포맷팅 적용
기능에는 변화가 없으며, types 항목을 멀티라인으로 정리해 읽기 쉽게 했습니다.

🧰 Tools
🪛 Biome (1.9.4)

[error] 26-26: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 26-26: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 26-28: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


30-33: include 배열 멀티라인 포맷팅 적용
기능에는 변화가 없으며, 프로젝트 포함 경로를 멀티라인으로 정렬해 유지보수를 용이하게 했습니다.

🧰 Tools
🪛 Biome (1.9.4)

[error] 30-30: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 30-30: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 30-33: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)

src/app/routes/Router.tsx (1)

77-77:

Details

❓ Verification inconclusive

인증 요구사항 확인 필요

티켓 옵션 편집 페이지가 requiresAuth: false로 설정되어 있습니다. 티켓 옵션 편집이 인증 없이 접근 가능한 기능인지 확인이 필요합니다.

다음 스크립트로 다른 대시보드 라우트들의 인증 설정을 확인해보세요:


🏁 Script executed:

#!/bin/bash
# 대시보드 관련 라우트들의 인증 설정 확인
grep -A 1 -B 1 "DASHBOARD_ROUTES\." src/app/routes/Router.tsx | grep "requiresAuth"

Length of output: 1407


인증 설정 일관성 확인 필요

현재 src/app/routes/Router.tsx의 모든 대시보드 관련 라우트에 requiresAuth: false가 설정되어 있으며, ticketOptionEdit도 동일하게 동작합니다.

  • 다른 페이지들과 인증 설정이 일관적으로 false로 적용되어 있습니다.

요구사항에 따라 대시보드 기능 전체에 인증이 필요하다면, 모든 해당 라우트의 requiresAuthtrue로 변경해야 합니다.
디자인 상 이 라우트들이 인증 없이 접근 가능한 것이 맞는지 확인 부탁드립니다.

src/app/Layout.tsx (1)

3-8: 좋은 구조적 개선입니다

불필요한 div 중첩을 제거하여 JSX 구조를 단순화한 것은 좋은 리팩토링입니다. 코드 가독성이 향상되었고 동일한 기능을 더 깔끔하게 구현했습니다.

src/features/ticket/ui/TicketOptionListSection.tsx (1)

8-10: 컴포넌트 구조가 깔끔합니다!

useTicketOptionForm 훅의 반환 타입을 활용한 props 정의가 적절합니다.

src/features/ticket/model/ticketOption.ts (1)

3-35: State와 Action 타입 정의가 명확합니다!

주석이 잘 작성되어 있고, 액션 타입이 체계적으로 정의되어 있습니다.

src/features/ticket/ui/TicketOptionFormSection.tsx (1)

51-55: 응답 형식 선택 시 상태 초기화가 적절합니다!

선택 변경 시 관련 경고와 포커스 상태를 초기화하는 로직이 잘 구현되어 있습니다.

src/features/dashboard/ui/DragArea.tsx (5)

6-7: 새로운 의존성 추가가 적절합니다.

TicketOptionsType 모델과 useParams 훅 추가로 타입 안전성과 라우팅 기능이 향상되었습니다.


10-15: Props 구조 단순화가 훌륭합니다.

복잡한 data prop을 단순한 options 배열로 변경하여 컴포넌트의 사용성과 가독성이 크게 향상되었습니다. activeButtonticketName props 추가도 적절합니다.


17-21: 기본값 설정과 라우팅 로직이 적절합니다.

  • ticketSurveyAddButton의 기본값을 false로 변경한 것이 합리적입니다
  • isTicketArea 조건에서 startsWith('ticket-') 체크 추가로 더 유연한 매칭이 가능해졌습니다
  • useParams를 통한 id 추출이 적절합니다

38-51: 렌더링 로직 개선이 우수합니다.

options 배열을 직접 매핑하는 방식으로 변경되어 코드가 더 직관적이고 효율적이 되었습니다. DraggableList에 전달되는 props들이 TicketOptionsType 인터페이스와 잘 매치됩니다.


57-60: 버튼 텍스트와 네비게이션 경로 업데이트가 적절합니다.

  • "티켓 설문"에서 "티켓 옵션"으로 용어 통일이 좋습니다
  • 동적 id 파라미터를 포함한 새로운 라우팅 경로가 적절히 적용되었습니다
src/features/ticket/api/ticketOption.ts (4)

6-9: 티켓 옵션 생성 함수가 잘 구현되었습니다.

API 엔드포인트와 타입 정의가 적절하며, Promise 기반 비동기 처리가 올바르게 구현되었습니다.


27-36: 조회 함수들이 잘 구현되었습니다.

티켓 옵션 목록 조회와 티켓에 부착된 옵션 조회 함수들이 적절하게 구현되었습니다. API 엔드포인트 경로도 RESTful 설계 원칙을 잘 따르고 있습니다.


39-42: 상세 조회 함수가 잘 구현되었습니다.

단일 티켓 옵션 상세 정보 조회가 적절하게 구현되었습니다.


45-54: 부착 함수 구현이 적절합니다.

POST 요청으로 쿼리 파라미터를 사용하는 방식이 적절하며, 빈 body와 함께 params 옵션을 사용한 구현이 올바릅니다.

src/features/dashboard/ui/DraggableList.tsx (8)

5-6: 새로운 의존성 추가가 적절합니다.

React Router 훅과 ticket option mutation 훅들을 추가하여 컴포넌트의 자립성이 향상되었습니다.


9-18: Props 인터페이스 개선이 우수합니다.

  • id prop을 optionIddraggableId로 분리하여 명확성 향상
  • activeButton prop 추가로 UI 제어 유연성 확보
  • 외부 의존성 제거로 컴포넌트 자립성 향상

31-34: mutation 훅 사용이 적절합니다.

외부 콜백 대신 React Query mutation 훅을 직접 사용하여 컴포넌트의 독립성과 재사용성이 향상되었습니다.


45-51: 부착 취소 핸들러가 잘 구현되었습니다.

droppableId에서 ticketId를 추출하는 로직과 mutation 호출이 적절하게 구현되었습니다.


54-60: 편집 핸들러 개선이 좋습니다.

localStorage 사용을 제거하고 라우트 파라미터와 state를 활용하는 방식으로 변경된 것이 더 명확하고 안전합니다.


63-68: 삭제 핸들러가 적절하게 구현되었습니다.

options 영역에서만 삭제가 가능하도록 하는 조건부 로직이 적절합니다.


86-95: 조건부 버튼 렌더링이 우수합니다.

activeButton prop에 따라 수정 또는 삭제 버튼을 선택적으로 표시하는 로직이 깔끔하고 유연합니다.


106-110: 티켓 영역의 삭제 버튼 구현이 적절합니다.

티켓 영역에서는 삭제가 아닌 부착 취소를 수행하도록 하는 로직이 올바르게 구현되었습니다.

src/features/ticket/hooks/useTicketOptionHook.ts (8)

1-14: import 문과 타입 정의가 적절합니다.

필요한 모든 의존성이 올바르게 import되었고, API 함수와 타입들이 적절히 가져와졌습니다.


16-30: 생성 mutation 훅이 잘 구현되었습니다.

eventId를 URL 파라미터에서 자동으로 추출하여 요청에 포함하는 방식이 깔끔합니다. 성공/실패 시 사용자 피드백과 네비게이션 처리도 적절합니다.


33-51: 수정 mutation 훅이 우수합니다.

쿼리 무효화를 통한 캐시 일관성 유지와 적절한 사용자 피드백이 잘 구현되었습니다. 관련 쿼리들을 모두 무효화하는 것도 좋습니다.


73-82: 티켓 옵션 목록 조회 훅이 적절합니다.

쿼리 키 전략과 enabled 조건이 올바르게 설정되어 있습니다.


85-91: 부착된 옵션 조회 훅이 잘 구현되었습니다.

ticketId를 파라미터로 받아 조건부로 활성화하는 방식이 적절합니다.


94-100: 상세 조회 훅이 적절합니다.

ticketOptionId 조건부 활성화가 올바르게 구현되었습니다.


103-117: 부착 mutation 훅이 잘 구현되었습니다.

적절한 쿼리 무효화와 로깅을 통한 피드백이 구현되어 있습니다.


120-134: 부착 취소 mutation 훅이 적절합니다.

부착과 일관된 구조로 구현되어 있으며, 적절한 쿼리 무효화가 수행됩니다.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (5)
src/features/ticket/hooks/useTicketOptionForm.ts (4)

30-41: 최소 옵션 개수 검증 로직 수정 필요

현재 로직은 옵션이 0개일 때만 삭제를 막지만, 최소 1개 이상 유지하려면 1개일 때도 삭제를 막아야 합니다.

-if (activeOptions.options.length < 1) {
+if (activeOptions.options.length <= 1) {

103-105: 응답 형식 타입 변환 필요

optionDetail.type은 영문('SINGLE', 'MULTIPLE', 'TEXT')이지만, SET_RESPONSE_TOGGLE은 한글 형식을 기대합니다.

타입 변환을 추가하세요:

-dispatch({ type: 'SET_RESPONSE_TOGGLE', payload: optionDetail.type });
+const responseFormat = optionDetail.type === 'SINGLE' ? '객관식' 
+  : optionDetail.type === 'MULTIPLE' ? '여러개 선택' 
+  : '자유로운 텍스트';
+dispatch({ type: 'SET_RESPONSE_TOGGLE', payload: responseFormat });

또는 SET_ALL 액션을 사용하는 것을 고려하세요.


142-142: 디버그용 console.log 제거 필요

프로덕션 코드에 디버그 로그가 남아있습니다.

-console.log('Clicked!');

153-153: querySelector 대신 React ref 사용 권장

한글 클래스명을 사용한 직접적인 DOM 조작은 취약합니다. React의 ref를 사용하는 것이 더 안전합니다.

다음과 같은 방법을 고려하세요:

  1. useRef를 사용하여 요소 참조 저장
  2. 스크롤 대상 컴포넌트에 ref 전달
  3. ref.current?.scrollIntoView() 사용

또한 '.질문-입력란' 같은 한글 클래스명보다는 'question-input' 같은 영문 클래스명이나 data 속성 사용을 권장합니다.

src/features/ticket/model/ticketOption.ts (1)

60-60: 빈 인터페이스 대신 타입 별칭 사용 권장

ESLint가 올바르게 지적한 대로, 추가 속성이 없는 인터페이스는 타입 별칭으로 대체하는 것이 좋습니다.

다음과 같이 수정하세요:

-export interface TicketOptionResponse extends ApiResponse<TicketOptionsType> {}
+export type TicketOptionResponse = ApiResponse<TicketOptionsType>;
🧰 Tools
🪛 ESLint

[error] 60-60: An interface declaring no members is equivalent to its supertype.

(@typescript-eslint/no-empty-object-type)

🧹 Nitpick comments (4)
src/features/ticket/hooks/useTicketOptionForm.ts (1)

181-193: 중복 로직 리팩터링 권장

타입 매핑과 choices 필터링 로직이 반복됩니다. 헬퍼 함수로 추출하는 것을 고려하세요.

+    const getChoicesForType = (responseFormat: string) => {
+      if (responseFormat === 'TEXT') return [];
+      
+      const options = responseFormat === 'SINGLE' 
+        ? state.singleOptions.options 
+        : state.multiOptions.options;
+      
+      return options.filter(opt => opt.trim() !== '');
+    };

     if (isValid) {
-      const type =
-        state.question.responseFormat === 'SINGLE'
-          ? 'SINGLE'
-          : state.question.responseFormat === 'MULTIPLE'
-          ? 'MULTIPLE'
-          : 'TEXT';
-
-      const choices =
-        state.question.responseFormat === 'SINGLE'
-          ? state.singleOptions.options.filter(opt => opt.trim() !== '')
-          : state.question.responseFormat === 'MULTIPLE'
-          ? state.multiOptions.options.filter(opt => opt.trim() !== '')
-          : [];
+      const type = state.question.responseFormat;
+      const choices = getChoicesForType(type);
src/features/ticket/model/ticketOption.ts (1)

43-43: TicketOptionRequest의 type 필드 타입 개선

type 필드가 string으로 정의되어 있지만, TicketOptionType을 사용하면 타입 안전성이 향상됩니다.

export interface TicketOptionRequest {
  eventId: number;
  name: string;
  description: string;
-  type: string;
+  type: TicketOptionType;
  isMandatory: boolean;
  choices: string[];
}
src/features/ticket/model/ticketOptionReducer.ts (2)

57-57: 타입 단언 대신 타입 가드 사용 권장

action.payload as TicketOptionType는 런타임에서 타입 안전성을 보장하지 않습니다. 타입 가드를 사용하는 것을 고려하세요.

+    const isValidTicketOptionType = (value: string): value is TicketOptionType => {
+      return ['SINGLE', 'MULTIPLE', 'TEXT'].includes(value);
+    };
+
     case 'SET_RESPONSE_TOGGLE':
+      if (!isValidTicketOptionType(action.payload)) {
+        console.warn(`Invalid ticket option type: ${action.payload}`);
+        return state;
+      }
       return {
         ...state,
         question: {
           ...state.question,
-          responseFormat: action.payload as TicketOptionType,
+          responseFormat: action.payload,
         },
       };

115-117: 불필요한 타입 변환 로직 제거 가능

type이 이미 TicketOptionType이라면 responseFormat 변환 로직이 불필요합니다.

     case 'SET_ALL': {
       const { name, description, type, isMandatory, choices } = action.payload;
-      // type에 따라 responseFormat 한글로 변환
-      let responseFormat = 'SINGLE';
-      if (type === 'MULTIPLE') responseFormat = 'MULTIPLE';
-      if (type === 'TEXT') responseFormat = 'TEXT';

       return {
         ...state,
         question: {
           ...state.question,
           title: name,
           description: description,
-          responseFormat: responseFormat as TicketOptionType,
+          responseFormat: type,
           answerToggled: isMandatory,
         },
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1d6b1a3 and e715d61.

📒 Files selected for processing (9)
  • src/features/dashboard/ui/DraggableList.tsx (2 hunks)
  • src/features/ticket/api/ticketOption.ts (1 hunks)
  • src/features/ticket/hooks/useTicketOptionDnD.ts (1 hunks)
  • src/features/ticket/hooks/useTicketOptionForm.ts (1 hunks)
  • src/features/ticket/model/ticketOption.ts (1 hunks)
  • src/features/ticket/model/ticketOptionReducer.ts (1 hunks)
  • src/features/ticket/ui/TicketOptionFormSection.tsx (1 hunks)
  • src/features/ticket/ui/TicketOptionListSection.tsx (1 hunks)
  • src/main.tsx (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (6)
  • src/features/ticket/ui/TicketOptionFormSection.tsx
  • src/features/ticket/ui/TicketOptionListSection.tsx
  • src/main.tsx
  • src/features/ticket/hooks/useTicketOptionDnD.ts
  • src/features/ticket/api/ticketOption.ts
  • src/features/dashboard/ui/DraggableList.tsx
🧰 Additional context used
🧬 Code Graph Analysis (3)
src/features/ticket/model/ticketOptionReducer.ts (1)
src/features/ticket/model/ticketOption.ts (3)
  • State (6-24)
  • Action (27-37)
  • TicketOptionType (3-3)
src/features/ticket/hooks/useTicketOptionForm.ts (3)
src/features/ticket/model/ticketOptionReducer.ts (4)
  • State (143-143)
  • Action (143-143)
  • ticketOptionReducer (25-141)
  • initialState (4-22)
src/features/ticket/model/ticketOption.ts (3)
  • State (6-24)
  • Action (27-37)
  • TicketOptionsType (48-58)
src/features/ticket/hooks/useTicketOptionHook.ts (3)
  • useCreateTicketOptionMutation (16-30)
  • useModifyTicketOptionMutation (33-51)
  • useGetTicketOptionDetail (94-100)
src/features/ticket/model/ticketOption.ts (2)
src/features/ticket/model/ticketOptionReducer.ts (2)
  • State (143-143)
  • Action (143-143)
src/shared/types/api/apiResponse.ts (1)
  • ApiResponse (1-5)
🪛 ESLint
src/features/ticket/model/ticketOption.ts

[error] 60-60: An interface declaring no members is equivalent to its supertype.

(@typescript-eslint/no-empty-object-type)

🔇 Additional comments (2)
src/features/ticket/model/ticketOptionReducer.ts (2)

82-84: 인덱스 범위 검증 추가 완료!

이전 검토에서 제안된 배열 인덱스 범위 검증이 적절히 구현되었습니다. 런타임 에러를 방지하는 좋은 개선입니다.

Also applies to: 101-103


120-120: choices 배열 안전성 검증 완료!

Array.isArray(choices) 검증을 통해 choices가 배열이 아닌 경우를 안전하게 처리하고 있습니다. 이전 검토 의견이 잘 반영되었습니다.

@xaexunxang xaexunxang force-pushed the feat/#132/ticket-option-api branch from 8cf78b3 to b510ece Compare May 30, 2025 13:51
@xaexunxang xaexunxang merged commit 0edde47 into develop May 30, 2025
1 of 2 checks passed
@xaexunxang xaexunxang deleted the feat/#132/ticket-option-api branch May 30, 2025 13:53
@github-actions
Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🔧 Feature 기능 구현

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[feat] 티켓 옵션 페이지 API 연동

2 participants