Skip to content
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
4777429
fix: 사용자 정보 수정 API 보안 위험 수정
Yejiin21 May 29, 2025
82a3322
feat: 토큰 없을 시 이벤트 상세 페이지 좋아요 UI 가리기
Yejiin21 May 29, 2025
114bac5
돋보기 이미지 클릭시 검색 페이지로 이동
Yejiin21 May 29, 2025
88ab663
feat: 이벤트 상세 페이지 링크 첨부 API data 연결
Yejiin21 May 29, 2025
80f76df
fix: 주소 정보 상세주소까지 포함하도록 수정
Yejiin21 May 29, 2025
8ef76b8
feat: 전체 페이지에서 이벤트 카드 클릭시 상세 페이지로 이동
Yejiin21 May 29, 2025
283d05b
fix: 종료된 이벤트 정보 안보이게 설정
Yejiin21 May 29, 2025
680ae56
refac: 공개 API에 대한 로직 처리
Yejiin21 May 31, 2025
4990363
fix: 이벤트 카드 한개일때도 이벤트 카드 크기 유지
Yejiin21 May 31, 2025
69aa24c
fix: 온라인일때는 주소 아이콘 안보이기
Yejiin21 May 31, 2025
a941f2c
refac: 전화번호 개수 에러 문구 추가
Yejiin21 May 31, 2025
31c8d3c
fix: 대시보드 이벤트 기본 정보 페이지 날짜 렌더링 에러 해결
Yejiin21 May 31, 2025
5607cf4
design: 주문번호, 참여자 정보 글씨 위치 헤더에 맞게 정렬
Yejiin21 May 31, 2025
ea3f2f5
design: 응답 요약 페이지 차트 영역 배경색 추가
Yejiin21 May 31, 2025
572b01e
refac: 요약페이지 질문 순서 정렬
Yejiin21 May 31, 2025
d37f51b
refac: 옵션 영역에서 삭제 시 해당 옵션 데이터 모두 삭제되도록 수정
Yejiin21 May 31, 2025
cdc6b45
feat: 옵션 삭제 제한조건 추가
Yejiin21 May 31, 2025
63513a7
fix: 날짜 인풋 초기값 현재 날짜로 설정
Yejiin21 May 31, 2025
7065b44
refact: 사용하지 않는 임포트 제거
Yejiin21 May 31, 2025
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
10 changes: 5 additions & 5 deletions src/entities/event/api/event.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { EventItem, PaginationParams } from '../model/eventDetail';
export const eventDetail = async (dto: EventDetailRequest) => {
const response = await axiosClient.get(`/events/${dto.eventId}`, {
params: { userId: dto.userId },
headers: { skipAuth: true },
headers: { isPublicApi: true },
});
return response.data;
};
Expand All @@ -25,7 +25,7 @@ export const getAllEventsInfinite = async ({
params.append('size', size.toString());

const response = await axiosClient.get<ApiResponse<EventItem[]>>(`/events?${params.toString()}`, {
headers: { skipAuth: true },
headers: { isPublicApi: true },
});

const items = response.data.result ?? [];
Expand All @@ -49,7 +49,7 @@ export const getCategoryEventsInfinite = async ({
params.append('size', size.toString());

const response = await axiosClient.get<ApiResponse<EventItem[]>>(`/events/categories?${params.toString()}`, {
headers: { skipAuth: true },
headers: { isPublicApi: true },
});

const items = response.data.result ?? [];
Expand All @@ -63,7 +63,7 @@ export const getCategoryEventsInfinite = async ({
// 태그별 이벤트 목록 조회 (최신, 인기, 마감 / 기본 정보)
export const getEventByTag = async (tag: TagType, { page, size }: PaginationParams): Promise<EventItem[]> => {
const response = await axiosClient.get<{ result: EventItem[] }>(`/events?tags=${tag}&page=${page}&size=${size}`, {
headers: { skipAuth: true },
headers: { isPublicApi: true },
});
return response.data.result || [];
};
Expand All @@ -76,7 +76,7 @@ export const getEventByCategory = async (
const response = await axiosClient.get<EventItem[]>(
`/events/category?category=${category}&page=${page}&size=${size}`,
{
headers: { skipAuth: true },
headers: { isPublicApi: true },
}
);
return response.data;
Expand Down
15 changes: 3 additions & 12 deletions src/features/dashboard/ui/MultiplePieCharts.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { PieChart, Pie, Cell, Legend, ResponsiveContainer } from 'recharts';
import { TicketOption, TicketOptionAnswer} from '../../ticket/model/ticketInformation';
import { TicketOption, TicketOptionAnswer } from '../../ticket/model/ticketInformation';

const COLORS = ['#FF6384', '#36A2EB', '#FFCE56', '#4BC0C0', '#9966FF', '#FF9F40'];

Expand All @@ -23,7 +23,7 @@ const MultiplePieCharts = ({ responses }: MultiplePieChartsProps) => {
}

return (
<div className="grid grid-cols-1 gap-6 p-4">
<div className="grid grid-cols-1 gap-6 p-4 bg-white">
{responses.map((option, index) => {
const data = aggregateAnswers(option.ticketOptionAnswers);

Expand All @@ -32,16 +32,7 @@ const MultiplePieCharts = ({ responses }: MultiplePieChartsProps) => {
<h3 className="mb-2 font-semibold text-lg">{option.optionName}</h3>
<ResponsiveContainer width="100%" height={300}>
<PieChart>
<Pie
data={data}
dataKey="value"
nameKey="name"
cx="50%"
cy="50%"
outerRadius={80}
label
stroke="none"
>
<Pie data={data} dataKey="value" nameKey="name" cx="50%" cy="50%" outerRadius={80} label stroke="none">
{data.map((_, i) => (
<Cell key={`cell-${i}`} fill={COLORS[i % COLORS.length]} />
))}
Expand Down
28 changes: 13 additions & 15 deletions src/features/dashboard/ui/ParicipantCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,38 +12,36 @@ interface ParticipantCardProps {
}

const ParticipantCard = ({ participant, onCheckClick }: ParticipantCardProps) => {

const { mutate: approveParticipant } = useApproveParticipants(participant.orderId);

return (
<div className="flex items-center justify-between w-full text-xs bg-white px-2 md:px-3 py-2 shadow-sm">
<div className="flex gap-2 md:gap-3">
<div className="flex items-center gap-4 md:gap-6 text-11 md:text-12">
<p>{participant.orderId}</p>
<div className="flex items-center gap-8 md:gap-20 text-10 md:text-12">
<p className="ml-3">{participant.orderId}</p>
<div className="flex flex-col">
<p>이름: {participant.participant}</p>
<p>이메일 주소: {participant.email}</p>
<p>휴대폰 번호: {participant.phoneNumber}</p>
<div>구매 일자: {formatDate(participant.purchaseDate)} {formatTime(participant.purchaseDate)}</div>
<div>
구매 일자: {formatDate(participant.purchaseDate)} {formatTime(participant.purchaseDate)}
</div>
<p>티켓 이름: {participant.ticketName}</p>
</div>
</div>
</div>
<div className="flex items-center justify-center gap-3">
{
<SecondaryButton
label="확인하기"
color="pink"
size="small"
onClick={onCheckClick}
/>
}
{participant.checkedIn ? <p className="text-[#888686]">완료</p> : <p className="text-[#888686]">미완료</p>}
{<SecondaryButton label="확인하기" color="pink" size="small" onClick={onCheckClick} />}
{participant.checkedIn ? (
<p className="text-[#888686] text-10 md:text-12">완료</p>
) : (
<p className="text-[#888686] text-10 md:text-12">미완료</p>
)}

{participant.approved ? (
<p className="text-[#888686]">승인됨</p>
<p className="text-[#888686] text-10 md:text-12">승인됨</p>
) : participant.ticketType === 'FIRST_COME' ? (
<p className="text-[#888686] invisible">승인됨</p>
<p className="text-[#888686] invisible text-10 md:text-12">승인됨</p>
) : (
<TertiaryButton
label="승인"
Expand Down
8 changes: 3 additions & 5 deletions src/features/dashboard/ui/PariticipantsList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ const ParticipantsList = ({ listType, selectedFilter = [], participants }: Parti
selectedTicketId,
selectedOrderId,
setSelectedTicketId,
setSelectedOrderId
setSelectedOrderId,
} = useParticipantStore();

const { data } = usePersonalTicketOptionAnswers(selectedTicketId);
Expand All @@ -38,9 +38,7 @@ const ParticipantsList = ({ listType, selectedFilter = [], participants }: Parti
return;
}

const order = data.result
.flatMap(user => user.orders)
.find(order => order.orderId === selectedOrderId);
const order = data.result.flatMap(user => user.orders).find(order => order.orderId === selectedOrderId);

if (!order) {
alert('응답 데이터가 없습니다.');
Expand Down Expand Up @@ -68,7 +66,7 @@ const ParticipantsList = ({ listType, selectedFilter = [], participants }: Parti
<div className="flex flex-col gap-2 mb-4">
<div className="flex justify-between text-xs text-[#888686] bg-white shadow-sm px-2 md:px-3 py-3 rounded-t-lg">
<div className="flex gap-2 md:gap-3">
<div className="flex items-center gap-15 md:gap-24">
<div className="flex items-center gap-3 md:gap-16">
<p>주문 번호</p>
<p>참여자 정보</p>
</div>
Expand Down
113 changes: 47 additions & 66 deletions src/features/dashboard/ui/ResponsesList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,85 +14,66 @@ interface ResponsesListProps {
const ResponsesList = ({ listType, ticketOptionResponses, ticketId }: ResponsesListProps) => {
const { data, isLoading, error } = usePersonalTicketOptionAnswers(ticketId);

const {
currentIndex,
setCurrentIndex,
} = useResponseStore();
const { currentIndex, setCurrentIndex } = useResponseStore();

useEffect(() => {
setCurrentIndex(() => 0);
}, [listType, setCurrentIndex]);

const renderTextResponses = (responses: TicketOption[]) => {
const textResponses = responses.filter((res) => res.optionType === 'TEXT');
if (textResponses.length === 0) return null;

return (
<>
{textResponses.map((textResponse, idx) => (
<div className="bg-white p-4 flex flex-col gap-2 mb-4" key={`${textResponse.optionId}-${idx}`}>
<div className="flex justify-between items-center text-xs bg-white px-2 md:px-3 py-3">
<p className="text-base font-bold">{textResponse.optionName}</p>
<p>응답 {textResponse.ticketOptionAnswers.length}개</p>
</div>

{textResponse.ticketOptionAnswers.length === 0 ? (
<p>응답이 없습니다.</p>
) : (
<div className="h-full max-h-48 overflow-y-auto space-y-2">
{textResponse.ticketOptionAnswers.map((answer) => (
<div
className="flex justify-between text-xs bg-gray-100 shadow-sm px-2 md:px-3 py-3 gap-2"
key={answer.id}
>
<p>{answer.answer}</p>
</div>
))}
</div>
)}
</div>
))}
</>
);
};

const renderList = () => {
switch (listType) {
case 'summary':
{
const filteredResponses = ticketOptionResponses.filter(
(option) => option.optionType !== 'TEXT'
);
return (
<>
<div className="flex justify-center">
<div style={{ minWidth: '300px', maxWidth: '600px', width: '100%' }}>
<MultiplePieCharts responses={filteredResponses} />
case 'summary': {
return (
<div className="space-y-4">
{ticketOptionResponses.map(option => {
if (option.optionType === 'TEXT') {
return (
<div key={option.optionId} className="bg-white p-4 rounded shadow-sm">
<div className="flex justify-between items-center text-xs px-2 md:px-3 py-3">
<p className="text-base font-bold">{option.optionName}</p>
<p>응답 {option.ticketOptionAnswers.length}개</p>
</div>
{option.ticketOptionAnswers.length === 0 ? (
<p className="text-sm text-gray-500">응답이 없습니다.</p>
) : (
<div className="max-h-48 overflow-y-auto space-y-2">
{option.ticketOptionAnswers.map(answer => (
<div key={answer.id} className="text-xs bg-gray-100 px-3 py-2 rounded">
{answer.answer}
</div>
))}
</div>
)}
</div>
);
}
return (
<div key={option.optionId} className="flex justify-center">
<div className="w-full max-w-[600px] min-w-[300px]">
<MultiplePieCharts responses={[option]} />
</div>
</div>
</div>
{renderTextResponses(ticketOptionResponses)}
</>
);
}
);
})}
</div>
);
}

case 'individual': {
if (isLoading) return <p>로딩 중...</p>;
if (error || !data?.result) return <p>데이터를 불러오지 못했습니다.</p>;
const allOrders = data.result.flatMap(user => user.orders);
return (
<IndividualResponseViewer orders={allOrders} currentIndex={currentIndex} setCurrentIndex={setCurrentIndex} />
);
}

case 'individual':
{
if (isLoading) return <p>로딩 중...</p>;
if (error || !data?.result) return <p>데이터를 불러오지 못했습니다.</p>;
const allOrders = data.result.flatMap(user => user.orders);
return (
<IndividualResponseViewer
orders={allOrders}
currentIndex={currentIndex}
setCurrentIndex={setCurrentIndex}
/>
);
}
default:
return null;
}
};

return <div>{renderList()}</div>;
};
export default ResponsesList;

export default ResponsesList;
22 changes: 12 additions & 10 deletions src/features/event/ui/DatePicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,12 @@ const EventDatePicker = ({
isLabel = false,
}: DatePickerProps) => {
const [startDate, setStartDate] = useState<Date | null>(
eventState?.startDate ? new Date(eventState.startDate) : new Date()
eventState?.startDate ? new Date(eventState.startDate) : initialStartDate ? new Date(initialStartDate) : new Date()
);
const [endDate, setEndDate] = useState<Date | null>(eventState?.endDate ? new Date(eventState.endDate) : new Date());

const [endDate, setEndDate] = useState<Date | null>(
eventState?.endDate ? new Date(eventState.endDate) : initialEndDate ? new Date(initialEndDate) : new Date()
);
const [startTime, setStartTime] = useState<string>('06:00');
const [endTime, setEndTime] = useState<string>('23:00');

Expand All @@ -39,18 +41,18 @@ const EventDatePicker = ({
const end = eventState?.endDate || initialEndDate;

if (start && !startDate) {
const startDate = new Date(start);
setStartDate(startDate);
const hours = startDate.getHours().toString().padStart(2, '0');
const minutes = startDate.getMinutes().toString().padStart(2, '0');
const startDateObj = new Date(start);
setStartDate(prev => prev ?? startDateObj);
const hours = startDateObj.getHours().toString().padStart(2, '0');
const minutes = startDateObj.getMinutes().toString().padStart(2, '0');
setStartTime(`${hours}:${minutes}`);
}

if (end && !endDate) {
const endDate = new Date(end);
setEndDate(endDate);
const hours = endDate.getHours().toString().padStart(2, '0');
const minutes = endDate.getMinutes().toString().padStart(2, '0');
const endDateObj = new Date(end);
setEndDate(prev => prev ?? endDateObj);
const hours = endDateObj.getHours().toString().padStart(2, '0');
const minutes = endDateObj.getMinutes().toString().padStart(2, '0');
setEndTime(`${hours}:${minutes}`);
}
}, [eventState, initialStartDate, initialEndDate]);
Expand Down
Loading