Conversation
|
Caution Review failedThe pull request is closed. ## Walkthrough
티켓 취소 기능이 대폭 개선되어, 사용자가 여러 티켓을 선택해 한 번에 취소할 수 있는 모드와 UI가 추가되었습니다. API 경로가 RESTful 스타일로 변경되었고, 타입 선언 및 컴포넌트 인터페이스가 확장되었습니다. 코드 스타일과 타입 일관성도 강화되었습니다.
## Changes
| 파일/경로 요약 | 변경 내용 요약 |
|------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------|
| src/features/ticket/api/order.ts | API import 스타일 변경, `cancelTickets` 함수의 엔드포인트를 RESTful 경로(`/orders/${orderId}/cancel`)로 변경 |
| src/features/ticket/hooks/useOrderHook.ts | 들여쓰기, 콤마 등 코드 스타일만 정비 |
| src/features/ticket/hooks/useTicketOptionHook.ts | import 정비, 타입 명시 강화, mutation 타입 변경(`TicketResponse` → `ApiResponse<null>`), ticketId null 체크 추가 |
| src/features/ticket/model/Order.ts | `OrderTicketResponse` 인터페이스 추가, import 보강, 기존 인터페이스 포맷 정비 |
| src/features/ticket/model/ticketInformation.ts | `TicketOptionType` 타입 별도 선언 및 적용, `CreateTicketRequest`에서 시간 필드 제거, 포맷 정비 |
| src/pages/menu/ui/MyTicketPage.ts | 다중 선택 티켓 취소 모드, 상태 및 UI 추가, 로컬 티켓 상태 관리, 취소 모달 연동, 타입 개선 등 대규모 기능 확장 |
| src/shared/ui/EventCard.tsx | `className` prop 추가 및 적용, 외부 스타일링 가능 |
| src/shared/ui/backgrounds/TicketHostLayout.tsx | `isCancelMode` prop 추가, 안내 메시지 조건부 변경 |
## Sequence Diagram(s)
```mermaid
sequenceDiagram
participant User
participant MyTicketPage
participant TicketHostLayout
participant EmailDeleteModal
participant API
User->>MyTicketPage: "티켓 취소" 모드 진입 버튼 클릭
MyTicketPage->>MyTicketPage: isCancelMode = true
MyTicketPage->>TicketHostLayout: isCancelMode 전달
User->>MyTicketPage: 여러 티켓 클릭(선택/해제)
User->>MyTicketPage: "선택 티켓 취소" 버튼 클릭
MyTicketPage->>MyTicketPage: 선택 티켓 없으면 alert
MyTicketPage->>EmailDeleteModal: 모달 오픈
User->>EmailDeleteModal: 취소 확정 클릭
EmailDeleteModal->>MyTicketPage: onApprove 호출
loop 선택된 각 티켓
MyTicketPage->>API: cancelTickets(orderId)
end
API-->>MyTicketPage: 응답 반환
MyTicketPage->>MyTicketPage: 티켓 목록에서 취소된 티켓 제거, 모달 닫기, 상태 초기화Possibly related PRs
Suggested reviewers
Poem
|
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (3)
src/features/ticket/model/Order.ts (1)
9-27: 포괄적인 응답 타입 정의 추가OrderTicketResponse 인터페이스가 잘 설계되었습니다. 이벤트 상세정보, 티켓 정보, 주문 상태를 포괄적으로 다루고 있어 타입 안전성이 크게 향상됩니다.
몇 가지 개선 제안:
remainDays가 문자열 타입인데, 숫자 타입이 더 적절할 수 있습니다startDate에 대한 명확한 포맷 정의를 JSDoc으로 추가하는 것을 고려해보세요다음과 같은 개선을 고려해보세요:
export interface OrderTicketResponse { id: number; event: { id: number; bannerImageUrl: string; title: string; hostChannelName: string; address: string; + /** ISO 8601 형식의 시작 날짜 */ startDate: string; + /** 남은 일수 */ - remainDays: string; + remainDays: number; hashtags: string[]; onlineType: OnlineType; }; ticketQrCode: string; ticketName: string; ticketPrice: number; orderStatus: 'COMPLETED' | 'PENDING' | 'CANCELED'; checkIn: boolean; }src/shared/ui/backgrounds/TicketHostLayout.tsx (1)
56-56: 사용자 친화적인 조건부 텍스트 표시취소 모드에 따른 조건부 텍스트 표시가 사용자 경험을 향상시킵니다. 명확한 안내 메시지를 제공합니다.
유지보수성 향상을 위해 텍스트를 상수로 분리하는 것을 고려해보세요.
다음과 같은 개선을 고려해보세요:
+const TICKET_MODE_MESSAGES = { + CANCEL: '취소할 티켓을 선택해주세요', + NORMAL: '티켓을 누르면 입장을 위한 QR코드를 확인할 수 있습니다.' +} as const; // ... -{isCancelMode ? '취소할 티켓을 선택해주세요' : '티켓을 누르면 입장을 위한 QR코드를 확인할 수 있습니다.'} +{isCancelMode ? TICKET_MODE_MESSAGES.CANCEL : TICKET_MODE_MESSAGES.NORMAL}src/pages/menu/ui/MyTicketPage.tsx (1)
46-46: 더 나은 UX를 위해 alert 대신 Toast나 Modal 사용을 고려해보세요.브라우저 기본 alert는 사용자 경험을 저해합니다.
-alert('취소할 티켓을 선택해주세요.'); +// Toast 컴포넌트 사용 예시 +showToast('취소할 티켓을 선택해주세요.', 'warning');
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (8)
src/features/ticket/api/order.ts(2 hunks)src/features/ticket/hooks/useOrderHook.ts(1 hunks)src/features/ticket/hooks/useTicketOptionHook.ts(1 hunks)src/features/ticket/model/Order.ts(1 hunks)src/features/ticket/model/ticketInformation.ts(3 hunks)src/pages/menu/ui/MyTicketPage.tsx(3 hunks)src/shared/ui/EventCard.tsx(3 hunks)src/shared/ui/backgrounds/TicketHostLayout.tsx(2 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (4)
src/features/ticket/hooks/useOrderHook.ts (2)
src/features/ticket/api/order.ts (3)
readTicket(4-17)cancelTickets(26-29)orderTickets(20-23)src/features/ticket/model/orderInformation.ts (1)
OrderTicketRequest(3-8)
src/features/ticket/api/order.ts (1)
src/shared/types/api/http-client.ts (1)
axiosClient(6-13)
src/features/ticket/hooks/useTicketOptionHook.ts (3)
src/features/ticket/model/ticketInformation.ts (4)
TicketOptionResponse(26-33)TicketOptionAnswerRequest(36-41)TicketOptionAnswerResponse(48-53)PersonalTicketOptionAnswerResponse(56-59)src/features/ticket/api/ticketOption.ts (4)
readTicketOptions(5-8)createTicketOptionAnswers(11-14)readPurchaserAnswers(17-22)readPersonalTicketOptionAnswers(25-30)src/shared/types/api/apiResponse.ts (1)
ApiResponse(1-5)
src/features/ticket/model/Order.ts (1)
src/shared/types/baseEventType.ts (1)
OnlineType(1-1)
⏰ Context from checks skipped due to timeout of 90000ms (1)
- GitHub Check: storybook
🔇 Additional comments (15)
src/features/ticket/api/order.ts (2)
1-2: 일관성 개선: 단일 따옴표 사용import 문에서 이중 따옴표를 단일 따옴표로 변경한 것은 코드 스타일 일관성을 위한 좋은 개선입니다.
27-27:Details
❓ Verification inconclusive
RESTful API 엔드포인트로 개선됨
API 엔드포인트가 쿼리 파라미터 방식에서 RESTful 경로 파라미터 방식으로 변경되어 더 나은 API 설계를 따르고 있습니다. 백엔드 API가 이 변경사항을 지원하는지 확인해주세요.
다음 스크립트를 실행하여 이 함수의 사용처를 확인해보세요:
🏁 Script executed:
#!/bin/bash # Description: cancelTickets 함수 사용처 확인 # Expected: 모든 호출부가 새로운 API 엔드포인트와 호환되는지 확인 rg -A 3 "cancelTickets" --type tsLength of output: 939
RESTful API 엔드포인트 적용 확인
cancelTickets함수와 그 호출부가 모두 새로운 경로(/orders/:orderId/cancel)로 변경된 것을 확인했습니다.
- src/features/ticket/api/order.ts:
cancelTickets함수 정의- src/features/ticket/hooks/useOrderHook.ts: 해당 함수 호출부
백엔드 API가 이 경로를 정상 지원하는지 한 번 더 확인 부탁드립니다.
src/features/ticket/model/Order.ts (1)
1-1: 적절한 타입 import 추가OnlineType import가 새로운 OrderTicketResponse 인터페이스에서 사용되어 타입 안전성을 제공합니다.
src/shared/ui/backgrounds/TicketHostLayout.tsx (2)
11-11: 취소 모드 지원을 위한 적절한 prop 추가
isCancelModeprop 추가가 잘 설계되었습니다. 선택적 boolean 타입으로 정의되어 기존 코드와의 호환성을 유지합니다.
19-19: 기본값 설정으로 호환성 보장
isCancelMode = false기본값 설정으로 기존 컴포넌트 사용처와의 호환성을 보장합니다.src/features/ticket/hooks/useOrderHook.ts (4)
7-11: 코드 포매팅 개선일관된 들여쓰기와 쉼표 배치로 코드 가독성이 향상되었습니다.
15-20: 일관된 포매팅 적용useTicketOrderDetail 훅의 포매팅이 다른 훅들과 일관성 있게 개선되었습니다.
24-33: useCancelTicket 훅 포매팅 개선mutation 설정의 포매팅이 깔끔하게 정리되어 가독성이 향상되었습니다. 성공/실패 콜백 처리도 적절합니다.
37-43: useOrderTicket 훅 포매팅 완료마지막 훅의 포매팅도 일관성 있게 개선되어 전체 파일의 코드 스타일이 통일되었습니다.
src/shared/ui/EventCard.tsx (1)
23-23: 좋은 개선입니다!
classNameprop 추가로 컴포넌트의 재사용성이 향상되었습니다. 외부에서 스타일을 주입할 수 있게 되어 MyTicketPage에서 선택된 카드의 시각적 피드백을 제공하는 데 활용되고 있습니다.Also applies to: 39-39, 54-54
src/features/ticket/hooks/useTicketOptionHook.ts (2)
1-14: 타입 일관성이 개선되었습니다!ApiResponse 타입 사용으로 API 응답 처리가 일관되게 되었습니다.
Also applies to: 27-27
43-46: 런타임 안전성이 향상되었습니다!ticketId null 체크를 추가하여 더 명시적인 에러 처리가 가능해졌습니다. enabled 조건과 함께 이중 방어 역할을 합니다.
Also applies to: 57-60
src/features/ticket/model/ticketInformation.ts (1)
1-1: DRY 원칙을 잘 적용한 리팩토링입니다!
TicketOptionType타입 별칭을 도입하여 코드 중복을 제거하고 유지보수성을 향상시켰습니다. 향후 옵션 타입이 변경되더라도 한 곳에서만 수정하면 됩니다.Also applies to: 30-30, 51-51, 66-66
src/pages/menu/ui/MyTicketPage.tsx (2)
26-31: 데이터 동기화가 잘 구현되었습니다.useEffect를 통해 서버 데이터와 로컬 상태를 동기화하여 취소된 티켓을 즉시 UI에서 제거할 수 있게 되었습니다.
84-86: 선택된 티켓의 시각적 피드백이 잘 구현되었습니다!scale과 border를 이용한 선택 표시가 직관적입니다. transition 효과도 부드러운 UX를 제공합니다.
| export interface CreateTicketRequest { | ||
| eventId: number; | ||
| ticketType: string; | ||
| ticketName: string; | ||
| ticketDescription: string; | ||
| ticketPrice: number; | ||
| availableQuantity: number; | ||
| startDate: string; | ||
| endDate: string; | ||
| } |
There was a problem hiding this comment.
💡 Verification agent
🧩 Analysis chain
API 스펙 변경이 반영되었습니다.
startTime과 endTime 필드가 제거되었습니다. 이 변경사항이 백엔드 API와 일치하는지 확인이 필요합니다.
다음 스크립트를 실행하여 CreateTicketRequest 사용처를 확인합니다:
🏁 Script executed:
#!/bin/bash
# Description: CreateTicketRequest 타입 사용처 확인
# CreateTicketRequest를 사용하는 코드 검색
ast-grep --pattern 'CreateTicketRequest'
# startTime 또는 endTime을 참조하는 코드가 있는지 확인
rg -A 5 'startTime|endTime' --glob '**/*.{ts,tsx}'Length of output: 8537
🏁 Script executed:
#!/bin/bash
# Show the contents of TicketContext.tsx to verify state shape
sed -n '1,200p' src/features/ticket/model/TicketContext.tsxLength of output: 1307
startTime·endTime 제거 반영: UI 상태 타입 및 구현 동기화 필요
백엔드 API 스펙에 맞춰 CreateTicketRequest에서 startTime·endTime을 제거하셨으나, 아래 UI 코드들이 여전히 해당 필드를 사용하고 있어 타입 불일치 및 런타임 오류가 발생합니다. API용 타입과 폼용 상태를 분리하거나, UI 상태 타입을 별도로 선언하여 구현을 수정해주세요.
주의할 파일 및 위치:
- src/features/ticket/model/TicketContext.tsx
•useState<CreateTicketRequest>({... startTime: '06:00', endTime: '23:00' }) - src/features/ticket/ui/TicketDatePicker.tsx
•onDateChange인자 · 훅 의존성 배열에startTime,endTime참조 - src/pages/dashboard/ui/ticket/TicketCreatePage.tsx
•ticketData상태에startTime,endTime할당 - 필요 시
CreateTicketRequest사용처 전체 점검
제안:
- CreateTicketRequest는 API 호출용으로만 사용하고, 폼 상태는 별도 인터페이스(ex.
TicketFormState)로 관리 - 폼 상태에서 날짜·시간을 합쳐
startDate,endDate만 API에 전달하도록 매핑 로직 추가
🤖 Prompt for AI Agents
In src/features/ticket/model/ticketInformation.ts lines 3 to 12, the
CreateTicketRequest interface was updated to remove startTime and endTime fields
to match backend API changes. However, UI code in TicketContext.tsx,
TicketDatePicker.tsx, and TicketCreatePage.tsx still references these removed
fields, causing type mismatches and runtime errors. To fix this, create a
separate interface (e.g., TicketFormState) for the UI form state that includes
startTime and endTime, keep CreateTicketRequest for API calls only, and
implement mapping logic to combine date and time fields into startDate and
endDate before sending data to the API. Update all affected UI components to use
the new form state interface instead of CreateTicketRequest directly.
| Promise.all(selectedIds.map(id => cancelTicket(id))).then(() => { | ||
| setTickets(prev => prev.filter(ticket => !selectedIds.includes(ticket.id))); | ||
| setIsDeleteModalOpen(false); | ||
| setIsCancelMode(false); | ||
| setSelectedIds([]); | ||
| }); | ||
| }} |
There was a problem hiding this comment.
🛠️ Refactor suggestion
에러 처리와 로딩 상태를 추가해야 합니다.
현재 구현은 다음 문제점들이 있습니다:
- 일부 취소 실패 시 전체가 실패할 수 있음
- 취소 중 로딩 표시가 없음
- 실패한 티켓에 대한 롤백 처리 없음
+const [isCancelling, setIsCancelling] = useState(false);
onClick={() => {
+ setIsCancelling(true);
- Promise.all(selectedIds.map(id => cancelTicket(id))).then(() => {
- setTickets(prev => prev.filter(ticket => !selectedIds.includes(ticket.id)));
- setIsDeleteModalOpen(false);
- setIsCancelMode(false);
- setSelectedIds([]);
- });
+ Promise.allSettled(selectedIds.map(id =>
+ new Promise((resolve) => {
+ cancelTicket(id, {
+ onSuccess: () => resolve({ id, success: true }),
+ onError: (error) => resolve({ id, success: false, error })
+ });
+ })
+ )).then(results => {
+ const successIds = results
+ .filter((result, index) => result.status === 'fulfilled' && result.value.success)
+ .map((_, index) => selectedIds[index]);
+
+ const failedCount = selectedIds.length - successIds.length;
+
+ if (successIds.length > 0) {
+ setTickets(prev => prev.filter(ticket => !successIds.includes(ticket.id)));
+ }
+
+ if (failedCount > 0) {
+ showToast(`${failedCount}개의 티켓 취소에 실패했습니다.`, 'error');
+ }
+
+ setIsCancelling(false);
+ setIsDeleteModalOpen(false);
+ setIsCancelMode(false);
+ setSelectedIds([]);
+ });
}}로딩 상태 표시를 위해 Modal에 isLoading={isCancelling} prop을 추가하는 것도 고려해보세요.
Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In src/pages/menu/ui/MyTicketPage.tsx around lines 137 to 143, the current
Promise.all call for cancelTicket lacks error handling, loading state
management, and rollback for failed cancellations. Introduce an isCancelling
state to track loading, set it to true before starting cancellations and false
after completion or error. Use Promise.allSettled to handle each cancellation
result individually, update tickets only for successfully cancelled ones, and
handle failures appropriately, possibly showing an error message. Also, pass
isCancelling as isLoading prop to the Modal to display loading status during
cancellation.
There was a problem hiding this comment.
Actionable comments posted: 0
♻️ Duplicate comments (1)
src/pages/menu/ui/MyTicketPage.tsx (1)
152-158:⚠️ Potential issue에러 처리와 로딩 상태 개선이 필요합니다.
현재 Promise.all 구현은 이전 리뷰에서 지적된 문제점들이 여전히 존재합니다:
- 일부 취소 실패 시 전체가 실패할 수 있음
- 취소 중 로딩 표시가 없음
- 실패한 티켓에 대한 개별 처리 없음
이전 리뷰 제안사항을 적용하여 Promise.allSettled 사용과 로딩 상태 관리를 구현해주세요.
🧹 Nitpick comments (1)
src/pages/menu/ui/MyTicketPage.tsx (1)
26-49: 유효성 검사 로직이 우수하지만 UX 개선이 필요합니다.이벤트 시작일 검증 로직이 잘 구현되어 있으나,
alert()사용은 사용자 경험 측면에서 개선이 필요합니다.다음과 같이 토스트 메시지로 개선하는 것을 고려해보세요:
- alert('이미 시작된 이벤트의 티켓은 취소할 수 없습니다.'); + showToast('이미 시작된 이벤트의 티켓은 취소할 수 없습니다.', 'error');
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
src/pages/menu/ui/MyTicketPage.tsx(3 hunks)
🔇 Additional comments (5)
src/pages/menu/ui/MyTicketPage.tsx (5)
3-3: 새로운 import들이 적절히 추가되었습니다.티켓 취소 기능을 위한 필요한 훅, 타입, 컴포넌트들이 올바르게 import되었습니다.
Also applies to: 10-13
17-21: 상태 변수 선언이 적절합니다.다중 티켓 취소 기능을 위한 상태 변수들이 적절한 타입으로 선언되었습니다. 특히
OrderTicketResponse타입 사용으로 타입 안전성이 향상되었습니다.
51-55: 데이터 동기화 로직이 올바릅니다.서버에서 받은 데이터를 로컬 상태로 동기화하는 useEffect가 적절히 구현되었습니다.
58-69: 취소 모드 UI가 잘 구현되었습니다.조건부 렌더링과 버튼 라벨 변경 로직이 사용자 친화적으로 구현되었습니다. 모달이 열려있을 때 버튼을 숨기는 것도 좋은 UX입니다.
90-101: 선택 모드 상호작용이 직관적입니다.취소 모드에서의 티켓 선택/해제 로직과 시각적 피드백(크기 조정, 테두리)이 사용자가 이해하기 쉽게 구현되었습니다.
hyeeuncho
left a comment
There was a problem hiding this comment.
티켓 취소가 구매 당시에만 가능 했었는데 잘 구현해주셨네요~ 고예진 짱짱 최고
주문 취소 API 에러 수정
내 티켓 페이지 주문 취소 기능 추가
Summary by CodeRabbit
New Features
UI 개선
기타 개선