Skip to content
Merged
2 changes: 1 addition & 1 deletion src/features/event/model/FunnelContext.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React, { createContext, ReactNode, useContext, useState } from 'react';
import { CreateEventRequest } from './event';
import { HostCreationRequest } from './hostCreation';
import { HostCreationRequest } from '../../host/model/host';

export interface FunnelState {
hostState: HostCreationRequest;
Expand Down
6 changes: 3 additions & 3 deletions src/features/ticket/api/order.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { axiosClient } from "../../../shared/types/api/http-client"
import { OrderTicketRequest } from "../model/orderInformation";
import { axiosClient } from '../../../shared/types/api/http-client';
import { OrderTicketRequest } from '../model/orderInformation';

export const readTicket = {
// 주문 티켓 전체 조회
Expand All @@ -24,6 +24,6 @@ export const orderTickets = async (data: OrderTicketRequest) => {

// 티켓 취소
export const cancelTickets = async (orderId: number) => {
const response = await axiosClient.post(`/orders/cancel?orderId=${orderId}`);
const response = await axiosClient.post(`/orders/${orderId}/cancel`);
return response.data;
};
50 changes: 25 additions & 25 deletions src/features/ticket/hooks/useOrderHook.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,40 +4,40 @@ import { OrderTicketRequest } from '../model/orderInformation';

// 주문 전체 조회
export const useTicketOrders = (page: number = 0, size: number = 10) => {
return useQuery({
queryKey: ['ticketOrders', page, size],
queryFn: () => readTicket.getAll(page, size),
});
return useQuery({
queryKey: ['ticketOrders', page, size],
queryFn: () => readTicket.getAll(page, size),
});
};

// 주문 상세 조회
export const useTicketOrderDetail = (orderId: number) => {
return useQuery({
queryKey: ['ticketOrderDetail', orderId],
queryFn: () => readTicket.getDetail(orderId),
enabled: !!orderId
});
return useQuery({
queryKey: ['ticketOrderDetail', orderId],
queryFn: () => readTicket.getDetail(orderId),
enabled: !!orderId,
});
};

// 주문 취소
export const useCancelTicket = () => {
return useMutation({
mutationFn: (orderId: number) => cancelTickets(orderId),
onSuccess: () => {
alert('티켓이 성공적으로 취소되었습니다.');
},
onError: () => {
alert('티켓 취소에 실패하였습니다.');
},
});
return useMutation({
mutationFn: (orderId: number) => cancelTickets(orderId),
onSuccess: () => {
alert('티켓이 성공적으로 취소되었습니다.');
},
onError: () => {
alert('티켓 취소에 실패하였습니다.');
},
});
};

// 티켓 구매
export const useOrderTicket = () => {
return useMutation({
mutationFn: (data: OrderTicketRequest) => orderTickets(data),
onError: () => {
alert("티켓 구매 중 오류가 발생했습니다.");
},
});
};
return useMutation({
mutationFn: (data: OrderTicketRequest) => orderTickets(data),
onError: () => {
alert('티켓 구매 중 오류가 발생했습니다.');
},
});
};
87 changes: 49 additions & 38 deletions src/features/ticket/hooks/useTicketOptionHook.ts
Original file line number Diff line number Diff line change
@@ -1,53 +1,64 @@
import { useMutation, useQuery } from "@tanstack/react-query";
import { PersonalTicketOptionAnswerResponse, TicketOptionAnswerRequest, TicketOptionAnswerResponse, TicketOptionResponse, TicketResponse } from "../model/ticketInformation";
import { createTicketOptionAnswers, readPersonalTicketOptionAnswers, readPurchaserAnswers, readTicketOptions } from "../api/ticketOption";
import { useMutation, useQuery } from '@tanstack/react-query';
import {
PersonalTicketOptionAnswerResponse,
TicketOptionAnswerRequest,
TicketOptionAnswerResponse,
TicketOptionResponse,
} from '../model/ticketInformation';
import {
createTicketOptionAnswers,
readPersonalTicketOptionAnswers,
readPurchaserAnswers,
readTicketOptions,
} from '../api/ticketOption';
import { ApiResponse } from '../../../shared/types/api/apiResponse';

// 티켓 옵션 조회
export const useTicketOptions = (ticketId: number) => {
return useQuery<{ isSuccess: boolean; result: TicketOptionResponse[] }>({
queryKey: ['ticketOptions', ticketId],
queryFn: () => readTicketOptions(ticketId),
enabled: !!ticketId,
});
return useQuery<{ isSuccess: boolean; result: TicketOptionResponse[] }>({
queryKey: ['ticketOptions', ticketId],
queryFn: () => readTicketOptions(ticketId),
enabled: !!ticketId,
});
};

// 티켓 옵션 응답 전송
export const useCreateTicketOptionAnswers = () => {
return useMutation<TicketResponse, Error, TicketOptionAnswerRequest>({
mutationFn: createTicketOptionAnswers,
onSuccess: () => {
console.log("티켓 옵션 응답 전송 성공");
},
onError: () => {
alert("티켓 옵션 응답 전송 중 오류가 발생했습니다.");
},
});
return useMutation<ApiResponse<null>, Error, TicketOptionAnswerRequest>({
mutationFn: createTicketOptionAnswers,
onSuccess: () => {
console.log('티켓 옵션 응답 전송 성공');
},
onError: () => {
alert('티켓 옵션 응답 전송 중 오류가 발생했습니다.');
},
});
};

// 티켓 옵션 응답 전체 조회
export const usePurchaserAnswers = (ticketId: number | null) => {
return useQuery<{ isSuccess: boolean; result: TicketOptionAnswerResponse[] }>({
queryKey: ['purchaserAnswers', ticketId],
queryFn: () => {
if (ticketId === null) {
throw new Error("ticketId is required");
}
return readPurchaserAnswers(ticketId);
},
enabled: !!ticketId,
});
return useQuery<{ isSuccess: boolean; result: TicketOptionAnswerResponse[] }>({
queryKey: ['purchaserAnswers', ticketId],
queryFn: () => {
if (ticketId === null) {
throw new Error('ticketId is required');
}
return readPurchaserAnswers(ticketId);
},
enabled: !!ticketId,
});
};

// 티켓 옵션 응답 개별 조회
export const usePersonalTicketOptionAnswers = (ticketId: number | null) => {
return useQuery<{ isSuccess: boolean; result: PersonalTicketOptionAnswerResponse[] }>({
queryKey: ['personalTicketOptionAnswers', ticketId],
queryFn: () => {
if (ticketId === null) {
throw new Error("ticketId is required");
}
return readPersonalTicketOptionAnswers(ticketId);
},
enabled: !!ticketId,
});
};
return useQuery<{ isSuccess: boolean; result: PersonalTicketOptionAnswerResponse[] }>({
queryKey: ['personalTicketOptionAnswers', ticketId],
queryFn: () => {
if (ticketId === null) {
throw new Error('ticketId is required');
}
return readPersonalTicketOptionAnswers(ticketId);
},
enabled: !!ticketId,
});
};
30 changes: 26 additions & 4 deletions src/features/ticket/model/Order.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,27 @@
import { OnlineType } from '../../../shared/types/baseEventType';

export interface OrderTicketRequest {
ticketId: number;
eventId: number;
ticketCnt: number;
}
ticketId: number;
eventId: number;
ticketCnt: number;
}

export interface OrderTicketResponse {
id: number;
event: {
id: number;
bannerImageUrl: string;
title: string;
hostChannelName: string;
address: string;
startDate: string;
remainDays: string;
hashtags: string[];
onlineType: OnlineType;
};
ticketQrCode: string;
ticketName: string;
ticketPrice: number;
orderStatus: 'COMPLETED' | 'PENDING' | 'CANCELED';
checkIn: boolean;
}
2 changes: 0 additions & 2 deletions src/features/ticket/model/TicketContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,6 @@ export const TicketProvider = ({ children }: { children: ReactNode }) => {
availableQuantity: 0,
startDate: '',
endDate: '',
startTime: '06:00',
endTime: '23:00',
});

const setTicketChannelId = (ticketChannelId: number) => {
Expand Down
68 changes: 30 additions & 38 deletions src/features/ticket/model/ticketInformation.ts
Original file line number Diff line number Diff line change
@@ -1,50 +1,43 @@
export interface CreateTicketRequest {
eventId: number;
ticketType: string;
ticketName: string;
ticketDescription: string;
ticketPrice: number;
availableQuantity: number;
startDate: string;
endDate: string;
startTime: string;
endTime: string;
}
export type TicketOptionType = 'SINGLE' | 'MULTIPLE' | 'TEXT';

export interface TicketResponse {
isSuccess: boolean;
code: string;
message: string;
result: string; // "ticketId: 2"
export interface CreateTicketRequest {
eventId: number;
ticketType: string;
ticketName: string;
ticketDescription: string;
ticketPrice: number;
availableQuantity: number;
startDate: string;
endDate: string;
}
Comment on lines +3 to 12
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

API 스펙 변경이 반영되었습니다.

startTimeendTime 필드가 제거되었습니다. 이 변경사항이 백엔드 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.tsx

Length 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.


export interface ReadTicketResponse {
ticketId: number;
ticketName: string;
ticketDescription: string;
ticketPrice: number;
availableQuantity: number;
ticketId: number;
ticketName: string;
ticketDescription: string;
ticketPrice: number;
availableQuantity: number;
}

export interface TicketOptionChoice {
id: number;
name: string;
id: number;
name: string;
}
export interface TicketOptionResponse {
id: number;
name: string;
description: string;
type: 'SINGLE' | 'MULTIPLE' | 'TEXT';
isMandatory: boolean;
choices: TicketOptionChoice[];
id: number;
name: string;
description: string;
type: TicketOptionType;
isMandatory: boolean;
choices: TicketOptionChoice[];
}

// 티켓 옵션 응답 전송
export interface TicketOptionAnswerRequest {
ticketOptionId: number;
answerText?: string;
ticketOptionChoiceId?: number;
ticketOptionChoiceIds?: number[];
ticketOptionId: number;
answerText?: string;
ticketOptionChoiceId?: number;
ticketOptionChoiceIds?: number[];
}

// 티켓 옵션 응답 전체 조회
Expand All @@ -55,11 +48,10 @@ export type TicketOptionAnswer = {
export type TicketOptionAnswerResponse = {
optionId: number;
optionName: string;
optionType: 'SINGLE' | 'MULTIPLE' | 'TEXT';
optionType: TicketOptionType;
answers: TicketOptionAnswer[];
};


// 티켓 옵션 응답 개별 조회
export type PersonalTicketOptionAnswerResponse = {
userId: number;
Expand All @@ -71,6 +63,6 @@ export interface Order {
}
export interface OptionAnswer {
optionName: string;
optionType: 'SINGLE' | 'MULTIPLE' | 'TEXT';
optionType: TicketOptionType;
answer: string;
}
}
Loading