Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion src/features/dashboard/model/email.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,5 @@ export interface ReadEmailResponse {
content: string;
recipients: string[];
reservationDate: string;
reservationTime: string;
targetName: string
}
17 changes: 6 additions & 11 deletions src/features/dashboard/model/store/TicketOptionStore.tsx
Original file line number Diff line number Diff line change
@@ -1,33 +1,28 @@
import { create } from "zustand";

export interface TOption {
type: string;
optionName: string;
required: boolean;
choices: string[];
}

interface TicketOptionState {
currentPage: number;
setCurrentPage: (page: number) => void;

selectedOptions: { [index: number]: { [key: string]: string | string[] } };
setOption: (index: number, optionName: string, value: string | string[]) => void;
selectedOptions: { [page: number]: { [optionId: number]: string | number | number[] } };
setOption: (page: number, optionId: number, value: string | number | number[]) => void;
resetOptions: () => void;
}

export const useTicketOptionStore = create<TicketOptionState>((set) => ({
currentPage: 1,
setCurrentPage: (page: number) => set({ currentPage: page }),

selectedOptions: {},
setOption: (index, optionName, value) => {
setOption: (index, optionId, value) => {
set((state) => {
const updatedSelectedOptions = { ...state.selectedOptions };
if (!updatedSelectedOptions[index]) {
updatedSelectedOptions[index] = {};
}
updatedSelectedOptions[index][optionName] = value;
updatedSelectedOptions[index][optionId] = value;
return { selectedOptions: updatedSelectedOptions };
});
},
resetOptions: () => set({ selectedOptions: {}, currentPage: 1 }),
}));
96 changes: 45 additions & 51 deletions src/features/payment/ui/TicketOption.tsx
Original file line number Diff line number Diff line change
@@ -1,60 +1,75 @@
import { TOption } from '../../dashboard/model/store/TicketOptionStore';
import { useTicketOptionStore } from '../../dashboard/model/store/TicketOptionStore';
import Checkbox from '../../../../design-system/ui/Checkbox';
import { useTicketOptionStore } from "../../dashboard/model/TicketOptionStore";
import Checkbox from "../../../../design-system/ui/Checkbox";
import { TicketOptionResponse } from "../../ticket/model/ticketInformation";

interface TicketOptionProps {
options: TOption[];
options: TicketOptionResponse[];
}

const TicketOption = ({ options }: TicketOptionProps) => {
const { currentPage, selectedOptions, setOption } = useTicketOptionStore();
const currentSelectedOptions = selectedOptions[currentPage] || {};

const handleChange = (type: string, optionName: string, value: string) => {
if (type === 'text' || type === 'single') {
setOption(currentPage, optionName, value);
} else if (type === 'multiple') {
const prevValues = (currentSelectedOptions[optionName] as string[]) || [];
const newValues = prevValues.includes(value) ? prevValues.filter(v => v !== value) : [...prevValues, value];
setOption(currentPage, optionName, newValues);
const handleChange = (
type: "text" | "single" | "multiple",
optionId: number,
value: string | number
) => {
if (type === "text") {
setOption(currentPage, optionId, value as string);
} else if (type === "single") {
setOption(currentPage, optionId, value as number);
} else if (type === "multiple") {
const prevValues = (currentSelectedOptions[optionId] as number[]) || [];
const newValues = prevValues.includes(value as number)
? prevValues.filter((v) => v !== value)
: [...prevValues, value as number];
setOption(currentPage, optionId, newValues);
}
console.log(selectedOptions)
};

return (
<div className="text-sm md:text-base font-semibold">
{options.map((option, index) => (
<div key={index} className="mt-4">
{options.map((option) => (
<div key={option.id} className="mt-4">
<p>
{option.optionName} {option.required && <span className="text-red-500">*</span>}
{option.name}
{/* {option.isMandatory && <span className="text-red-500">*</span>} */}
</p>

{option.type === 'text' && (
{option.type === "TEXT" && (
<input
type="text"
className="w-full p-2 border rounded-md mt-2"
placeholder="입력해주세요"
value={currentSelectedOptions[option.optionName] || ''}
onChange={e => handleChange('text', option.optionName, e.target.value)}
value={(currentSelectedOptions[option.id] as string) || ""}
onChange={(e) =>
handleChange("text", option.id, e.target.value)
}
/>
)}

{option.type === 'single' &&
option.choices.map(choice => (
{option.type === "SINGLE" &&
option.choices.map((choice) => (
<Checkbox
key={choice}
label={choice}
checked={currentSelectedOptions[option.optionName] === choice}
onChange={() => handleChange('single', option.optionName, choice)}
key={choice.id}
label={choice.name}
checked={currentSelectedOptions[option.id] === choice.id}
onChange={() => handleChange("single", option.id, choice.id)}
className="block mt-2"
/>
))}

{option.type === 'multiple' &&
option.choices.map(choice => (
{option.type === "MULTIPLE" &&
option.choices.map((choice) => (
<Checkbox
key={choice}
label={choice}
checked={(currentSelectedOptions[option.optionName] as string[])?.includes(choice)}
onChange={() => handleChange('multiple', option.optionName, choice)}
key={choice.id}
label={choice.name}
checked={(
(currentSelectedOptions[option.id] as number[]) || []
).includes(choice.id)}
onChange={() => handleChange("multiple", option.id, choice.id)}
className="block mt-2"
/>
))}
Expand All @@ -63,26 +78,5 @@ const TicketOption = ({ options }: TicketOptionProps) => {
</div>
);
};
export default TicketOption;

// 임의 데이터
export const options: TOption[] = [
{
type: 'text',
optionName: '성함을 알려주세요.',
required: true,
choices: [''],
},
{
type: 'single',
optionName: '티셔츠 사이즈 선택해주세요.',
required: true,
choices: ['S', 'M', 'L', 'XL'],
},
{
type: 'multiple',
optionName: '선택해주세요.',
required: false,
choices: ['1', '2', '3', '4'],
},
];
export default TicketOption;
4 changes: 2 additions & 2 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/order';
import { axiosClient } from "../../../shared/types/api/http-client"
import { OrderTicketRequest } from "../model/orderInformation";

export const readTicket = {
// 주문 티켓 전체 조회
Expand Down
18 changes: 14 additions & 4 deletions src/features/ticket/api/ticket.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { axiosClient } from '../../../shared/types/api/http-client';
import { CreateTicketRequest, ReadTicketResponse } from '../model/ticket';
import { axiosClient } from "../../../shared/types/api/http-client";
import { CreateTicketRequest, ReadTicketResponse, TicketOptionAnswerRequest, TicketOptionResponse } from "../model/ticketInformation";

export const createTicket = async (data: CreateTicketRequest) => {
const response = await axiosClient.post('/tickets', data);
Expand All @@ -14,6 +14,16 @@ export const readTicket = async (eventId: number): Promise<{ isSuccess: boolean;
};

export const deleteTicket = async (ticketId: number) => {
const response = await axiosClient.delete(`/tickets/${ticketId}`);
return response.data;
const response = await axiosClient.delete(`/tickets/${ticketId}`);
return response.data;
}

export const readTicketOptions = async (ticketId: number): Promise<{ isSuccess: boolean; result: TicketOptionResponse[] }> => {
const response = await axiosClient.get(`/ticket-options/tickets/${ticketId}`);
return response.data;
};

export const createTicketOptionAnswers = async (answers: TicketOptionAnswerRequest) => {
const response = await axiosClient.post("/ticket-option-answers", answers);
return response.data;
};
43 changes: 43 additions & 0 deletions src/features/ticket/hooks/useOrderHook.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { useMutation, useQuery } from '@tanstack/react-query';
import { cancelTickets, orderTickets, readTicket } from '../api/order';
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),
});
};

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

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

// 티켓 구매
export const useOrderTicket = () => {
return useMutation({
mutationFn: (data: OrderTicketRequest) => orderTickets(data),
onError: () => {
alert("티켓 구매 중 오류가 발생했습니다.");
},
});
};
28 changes: 23 additions & 5 deletions src/features/ticket/hooks/useTicketHook.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import { useMutation, useQuery } from '@tanstack/react-query';
import { createTicket, deleteTicket, readTicket } from '../api/ticket';
import { CreateTicketRequest, ReadTicketResponse } from '../model/ticket';
import { ApiResponse } from '../../../shared/types/api/apiResponse';
import { AxiosError } from 'axios';
import { useMutation, useQuery } from "@tanstack/react-query";
import { createTicket, createTicketOptionAnswers, deleteTicket, readTicket, readTicketOptions } from "../api/ticket";
import { CreateTicketRequest, ReadTicketResponse, TicketOptionAnswerRequest, TicketOptionResponse, TicketResponse } from "../model/ticketInformation";

export const useTickets = (eventId: number) => {
return useQuery<{ isSuccess: boolean; result: ReadTicketResponse[] }>({
Expand Down Expand Up @@ -37,3 +35,23 @@ export const useDeleteTicket = () => {
},
});
};

export const useTicketOptions = (ticketId: number) => {
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("티켓 옵션 응답 전송 중 오류가 발생했습니다.");
},
});
};
19 changes: 19 additions & 0 deletions src/features/ticket/model/orderInformation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
export interface OrderTicketRequest {
ticketId: number;
eventId: number;
ticketCnt: number;
}
export interface TicketConfirm {
id: number;
title: string;
startDate: string;
ticketName: string;
hostChannelName: string;
hostChannelDescription: string;
organizerEmail: string;
organizerPhoneNumber: string;
eventAddress: string;
locationLat: number;
locationLng: number;
orderStatus: "COMPLETED" | "PENDING" | "CANCELLED";
};
59 changes: 59 additions & 0 deletions src/features/ticket/model/ticketInformation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
export interface CreateTicketRequest {
eventId: number;
ticketType: string;
ticketName: string;
ticketDescription: string;
ticketPrice: number;
availableQuantity: number;
startDate: string;
endDate: string;
startTime: string;
endTime: string;
}

export interface TicketResponse {
isSuccess: boolean;
code: string;
message: string;
result: string; // "ticketId: 2"
}

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

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

// 텍스트
export interface TicketOptionAnswerTextRequest {
ticketOptionId: number;
answerText: string;
}

// 선택지
export interface TicketOptionAnswerChoiceRequest {
ticketOptionId: number;
ticketOptionChoiceId: number;
}

// 티켓 옵션 응답
export interface TicketOptionAnswerRequest {
ticketOptionId: number;
answerText?: string;
ticketOptionChoiceId?: number;
}
Loading