Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
4 changes: 2 additions & 2 deletions design-system/ui/Header.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { ReactElement, ButtonHTMLAttributes } from 'react';
import React, { ReactElement, ButtonHTMLAttributes, ReactNode } from 'react';
import TextButton from './buttons/TextButton'; // TextButton 컴포넌트를 가져옵니다.

type Button = ReactElement<ButtonHTMLAttributes<HTMLButtonElement>>;
Expand All @@ -7,7 +7,7 @@ type CenterContent = string | ReactElement<React.InputHTMLAttributes<HTMLInputEl

interface HeaderProps {
centerContent?: CenterContent; // 가운데 콘텐츠 (타이틀 or 검색창)
leftButtonLabel?: string; // 왼쪽 콘텐츠 (버튼)
leftButtonLabel?: ReactNode; // 왼쪽 콘텐츠 (버튼)
leftButtonClick?: () => void; // 왼쪽 버튼 클릭 핸들러
leftButtonClassName?: string; // 왼쪽 버튼 추가 스타일링 클래스
rightContent?: Button; // 오른쪽 콘텐츠 (버튼)
Expand Down
4 changes: 1 addition & 3 deletions design-system/ui/buttons/TertiaryButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ const TertiaryButton = ({ label, type, color, size, onClick, className }: Tertia
const baseStyle = `flex justify-center items-center border rounded`;

const sizeClasses = {
small: 'px-1.5 py-0.5 text-11 md:px-2 md:text-11',
small: 'px-1.5 py-0.5 text-10 md:px-2 md:text-11',
medium: 'px-4 py-1 text-sm',
large:
'text-sm sm:px-2.5 sm:py-2 sm:text-xs sm:rounded md:px-3 md:py-2.5 md:text-sm md:rounded-md lg:px-3 lg:py-2.5 lg:text-base lg:rounded-md',
Expand All @@ -30,5 +30,3 @@ const TertiaryButton = ({ label, type, color, size, onClick, className }: Tertia
};

export default TertiaryButton;


3 changes: 3 additions & 0 deletions public/assets/menu/Delete.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 8 additions & 0 deletions public/assets/menu/HomeButton.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ export const getEventByCategory = async (
};

// 이벤트 삭제 (DELETE)
export const deleteEvent = async (eventId: number): Promise<ApiResponse<string>> => {
const response = await axiosClient.delete<ApiResponse<string>>(`/events/${eventId}`);
export const eventDeletion = async (eventId: number) => {
const response = await axiosClient.delete(`/events/${eventId}`);
return response.data;
};
Comment on lines +76 to 79
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

함수 리팩토링의 일관성과 타입 안전성을 검토해주세요.

이벤트 삭제 함수가 deleteEvent에서 eventDeletion으로 이름이 변경되고, 타입 어노테이션이 제거되었습니다. 몇 가지 확인이 필요합니다:

  1. 명명 일관성: 다른 API 함수들과 네이밍 컨벤션이 일치하는지 확인
  2. 타입 안전성: 명시적인 반환 타입 제거로 인한 타입 추론 문제 가능성
  3. 사용처 업데이트: 이 함수를 사용하는 모든 곳에서 새 함수명으로 업데이트되었는지 확인

다음 스크립트로 함수 사용처와 일관성을 확인해보겠습니다:

타입 안전성을 위해 명시적인 반환 타입을 유지하는 것을 권장합니다:

-export const eventDeletion = async (eventId: number) => {
-  const response = await axiosClient.delete(`/events/${eventId}`);
+export const eventDeletion = async (eventId: number): Promise<ApiResponse<string>> => {
+  const response = await axiosClient.delete<ApiResponse<string>>(`/events/${eventId}`);
   return response.data;
 };

🏁 Script executed:

#!/bin/bash
# 기존 deleteEvent 함수 사용처가 남아있는지 확인
echo "=== 기존 deleteEvent 함수 사용처 확인 ==="
rg -i "deleteEvent" --type ts --type tsx

echo "=== 새로운 eventDeletion 함수 사용처 확인 ==="
rg -i "eventDeletion" --type ts --type tsx

echo "=== API 함수들의 네이밍 패턴 확인 ==="
rg -A 1 "export const.*=" src/entities/event/api/event.ts

Length of output: 1097


API 함수 eventDeletion에 명시적 반환 타입 및 제네릭 타입 추가 필요

src/entities/event/api/event.ts 내 다른 API 함수들과 비교했을 때, 반환 타입 어노테이션과 axiosClient.delete 제네릭 타입이 빠져 있어 타입 안전성이 떨어집니다.

확인 및 조치 사항:

  • 반환 타입을 명시적으로 선언 (Promise<ApiResponse<…>>)
  • axiosClient.delete<…> 제네릭 타입을 추가
  • 이 함수를 호출하는 모든 곳이 eventDeletion으로 변경되었는지 재검토

예시 변경안:

-export const eventDeletion = async (eventId: number) => {
-  const response = await axiosClient.delete(`/events/${eventId}`);
+export const eventDeletion = async (eventId: number): Promise<ApiResponse<void>> => {
+  const response = await axiosClient.delete<ApiResponse<void>>(`/events/${eventId}`);
   return response.data;
};

추가로, API 함수 네이밍 컨벤션을 일관되게 유지하고 싶다면

  • 동사+주체(예: deleteEvent, getEventDetail)
  • 또는 모두 noun+action(예: eventDeletion, eventDetail)
    중 하나로 통일하는 방안을 검토해주세요.
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export const eventDeletion = async (eventId: number) => {
const response = await axiosClient.delete(`/events/${eventId}`);
return response.data;
};
export const eventDeletion = async (eventId: number): Promise<ApiResponse<void>> => {
const response = await axiosClient.delete<ApiResponse<void>>(`/events/${eventId}`);
return response.data;
};
🤖 Prompt for AI Agents
In src/entities/event/api/event.ts around lines 76 to 79, the function
eventDeletion lacks explicit return type annotation and does not specify a
generic type for axiosClient.delete, which reduces type safety. Add a specific
return type, such as Promise<ApiResponse<YourType>>, and include the appropriate
generic type parameter in the delete call. Also, verify that all usage sites
have been updated to use eventDeletion instead of previous function names, and
consider standardizing the naming convention across API functions for
consistency.

16 changes: 12 additions & 4 deletions src/entities/event/hook/useEventHook.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { useQuery } from '@tanstack/react-query';
import { eventDetail } from '../api/eventDetail';
import { useMutation, useQuery } from '@tanstack/react-query';
import { eventDeletion, eventDetail } from '../api/event';
import { useParams } from 'react-router-dom';
import { useUserInfo } from '../../../features/join/hooks/useUserHook';
import { ApiResponse } from '../../../shared/types/api/apiResponse';

const useEventDetail = () => {
export const useEventDetail = () => {
const { id } = useParams();
const { data: user } = useUserInfo();

Expand All @@ -17,4 +18,11 @@ const useEventDetail = () => {

return { data };
};
export default useEventDetail;

export const useEventDeletion = () => {
return useMutation<ApiResponse<null>, Error, number>({
mutationFn: async (eventId: number) => {
return await eventDeletion(eventId);
},
});
};
2 changes: 1 addition & 1 deletion src/entities/event/hook/useEventListHook.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { EventList } from '../../../features/event/model/event';
import { useInfiniteScroll } from '../../../shared/hooks/useInfiniteScroll';
import { getAllEventsInfinite } from '../api/eventDetail';
import { getAllEventsInfinite } from '../api/event';

const useEventList = () => {
const { data, fetchNextPage, hasNextPage, isFetching } = useInfiniteScroll<EventList>({
Expand Down
1 change: 1 addition & 0 deletions src/features/event/ui/EventFunnel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ const EventFunnel = ({ onNext, onPrev, Funnel, Step, currentStep }: EventFunnelI
title="이벤트 정보를 입력해주세요"
onNext={() => handleNext(String(currentStep + 1))}
onPrev={() => onPrev(String(currentStep - 1))}
requireValidation={true}
>
<EventInfoPage />
</EventRegisterLayout>
Expand Down
2 changes: 1 addition & 1 deletion src/features/event/ui/EventList.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { useRef, useEffect } from 'react';
import { useInfiniteScroll } from '../../../shared/hooks/useInfiniteScroll';
import { getAllEventsInfinite, getCategoryEventsInfinite } from '../../../entities/event/api/eventDetail';
import { getAllEventsInfinite, getCategoryEventsInfinite } from '../../../entities/event/api/event';
import EventCard from '../../../shared/ui/EventCard';
import { BaseEvent, CategoryType, TagType } from '../../../shared/types/baseEventType';
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
Expand Down
10 changes: 9 additions & 1 deletion src/features/event/ui/FileUpload.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
import FileUploadImage from '../../../../public/assets/event-manage/creation/FileUpload.svg';
import { FunnelState } from '../model/FunnelContext';
import useImageUpload from '../../../shared/hooks/useImageUpload';
import { useEffect } from 'react';

interface FileUploadProps {
value?: string;
onChange?: (url: string) => void;
setEventState?: React.Dispatch<React.SetStateAction<FunnelState['eventState']>>;
useDefaultImage?: boolean;
onValidationChange?: (isValid: boolean) => void;
}

const FileUpload = ({ value, onChange, setEventState, useDefaultImage }: FileUploadProps) => {
const FileUpload = ({ value, onChange, setEventState, useDefaultImage, onValidationChange }: FileUploadProps) => {
const { previewUrl, fileInputRef, handleFileChange, handleDrop, setIsDragging, isDragging } = useImageUpload({
value, // 서버에서 받아온 기본 이미지
onSuccess: url => {
Expand All @@ -19,6 +21,12 @@ const FileUpload = ({ value, onChange, setEventState, useDefaultImage }: FileUpl
useDefaultImage,
});

useEffect(() => {
if (onValidationChange) {
onValidationChange(!!previewUrl);
}
}, [previewUrl, onValidationChange]);

return (
<div className="flex flex-col justify-start gap-1">
<h1 className="font-bold text-black text-lg">배너 사진 첨부</h1>
Expand Down
2 changes: 1 addition & 1 deletion src/features/home/hooks/useEventHook.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useQuery } from '@tanstack/react-query';
import { getEventByTag } from '../../../entities/event/api/eventDetail';
import { getEventByTag } from '../../../entities/event/api/event';
import { TagType } from '../../../shared/types/baseEventType';
import { EventItem } from '../../../entities/event/model/eventDetail';

Expand Down
2 changes: 1 addition & 1 deletion src/pages/dashboard/ui/EventDetailPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ import FileUpload from '../../../features/event/ui/FileUpload';
import LinkInput, { Link } from '../../../features/event/ui/LinkInput';
import TextEditor from '../../../features/event/ui/TextEditor';
import DashboardLayout from '../../../shared/ui/backgrounds/DashboardLayout';
import useEventDetail from '../../../entities/event/hook/useEventHook';
import { useUpdateEventHook } from '../../../features/dashboard/hook/useEventHook';
import { UpdateEventRequest } from '../../../features/dashboard/model/event';
import { OnlineType } from '../../../shared/types/baseEventType';
import { useEventDetail } from '../../../entities/event/hook/useEventHook';

const EventDetailPage = () => {
const navigate = useNavigate();
Expand Down
2 changes: 1 addition & 1 deletion src/pages/dashboard/ui/EventInfoPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@ import EventDatePicker from '../../../features/event/ui/DatePicker';
import DashboardLayout from '../../../shared/ui/backgrounds/DashboardLayout';
import { useNavigate } from 'react-router-dom';
import Button from '../../../../design-system/ui/Button';
import useEventDetail from '../../../entities/event/hook/useEventHook';
import { useUpdateEventHook } from '../../../features/dashboard/hook/useEventHook';
import { OnlineType } from '../../../shared/types/baseEventType';
import { AddressSearch } from '../../../shared/ui/AddressSearch';
import KakaoMap from '../../../shared/ui/KakaoMap';
import { UpdateEventRequest } from '../../../features/dashboard/model/event';
import { useEventDetail } from '../../../entities/event/hook/useEventHook';

const EventInfoPage = () => {
const navigate = useNavigate();
Expand Down
2 changes: 1 addition & 1 deletion src/pages/event/ui/EventDetailsPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ import dateImg from '../../../../public/assets/event-manage/details/Date.svg';
import timeImg from '../../../../public/assets/event-manage/details/Time.svg';
import locationImg from '../../../../public/assets/event-manage/details/Location.svg';
import KakaoMap from '../../../shared/ui/KakaoMap';
import useEventDetail from '../../../entities/event/hook/useEventHook';
import { useCreateBookmark, useDeleteBookmark } from '../../../features/bookmark/hook/useBookmarkHook';
import { formatDate, formatTime } from '../../../shared/lib/date';
import { useEventDetail } from '../../../entities/event/hook/useEventHook';

const EventDetailsPage = () => {
const navigate = useNavigate();
Expand Down
8 changes: 6 additions & 2 deletions src/pages/event/ui/create-event/EventInfoPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,15 @@ import TextEditor from '../../../../features/event/ui/TextEditor';
import LinkInput from '../../../../features/event/ui/LinkInput';
import { useFunnelState } from '../../../../features/event/model/FunnelContext';

const EventInfoPage = () => {
interface EventInfoPageProps {
onValidationChange?: (isValid: boolean) => void;
}

const EventInfoPage = ({ onValidationChange }: EventInfoPageProps) => {
const { setEventState } = useFunnelState();
return (
<div className="w-full px-5 space-y-8">
<FileUpload setEventState={setEventState} useDefaultImage={false} />
<FileUpload setEventState={setEventState} useDefaultImage={false} onValidationChange={onValidationChange} />
<TextEditor setEventState={setEventState} />
<LinkInput setEventState={setEventState} />
</div>
Expand Down
50 changes: 36 additions & 14 deletions src/pages/menu/ui/myHost/MyHostPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,13 @@ import EventCard from '../../../../shared/ui/EventCard';
import { useState } from 'react';
import useHostChannelList from '../../../../entities/host/hook/useHostChannelListHook';
import useHostDetail from '../../../../entities/host/hook/useHostDetailHook';
import TertiaryButton from '../../../../../design-system/ui/buttons/TertiaryButton';

const MyHostPage = () => {
const [selectedHostId, setSelectedHostId] = useState<number | null>(null);
const [deleteBtn, setDeleteBtn] = useState(false);
const [deletedEventId, setDeletedEventId] = useState<number[]>([]);

const { data } = useHostChannelList();
const { data: hostDetail } = useHostDetail(selectedHostId ?? 0);

Expand Down Expand Up @@ -35,21 +39,39 @@ const MyHostPage = () => {
)}
</div>

{/* 이벤트 카드 목록 */}
<div className="grid grid-cols-2 gap-4 mx-5 mt-3 md:grid-cols-2 lg:grid-cols-2 pb-6">
{hostDetail?.result?.events?.map(event => (
<EventCard
key={event.id}
id={event.id}
img={event.bannerImageUrl}
eventTitle={event.title}
dDay={event.remainDays}
host={event.hostChannelName}
eventDate={event.startDate}
location={event.onlineType}
hashtags={event.hashtags}
{hostDetail?.result?.events && hostDetail?.result?.events?.length > 0 && (
<div className="flex justify-end mx-6">
<TertiaryButton
label={deleteBtn ? '완료' : '삭제'}
type="button"
color="pink"
size="small"
onClick={() => setDeleteBtn(prev => !prev)}
/>
))}
</div>
)}

{/* 이벤트 카드 목록 */}
<div className="grid grid-cols-2 gap-4 mx-5 md:grid-cols-2 lg:grid-cols-2 pb-6">
{hostDetail?.result?.events
?.filter(event => !deletedEventId.includes(event.id))
.map(event => (
<EventCard
key={event.id}
id={event.id}
img={event.bannerImageUrl}
eventTitle={event.title}
dDay={event.remainDays}
host={event.hostChannelName}
eventDate={event.startDate}
location={event.onlineType}
hashtags={event.hashtags}
isDelete={deleteBtn}
onDeleteSuccess={(deletedEventId: number) => {
setDeletedEventId(prev => [...prev, deletedEventId]);
}}
/>
))}
</div>
</TicketHostLayout>
);
Expand Down
65 changes: 53 additions & 12 deletions src/shared/ui/EventCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@ import Countdown from '../../../design-system/ui/texts/Countdown';
import dateImg from '../../../public/assets/event-manage/details/Date.svg';
import locationImg from '../../../public/assets/event-manage/details/Location.svg';
import { formatDate } from '../lib/date';
import IconButton from '../../../design-system/ui/buttons/IconButton';
import deleteButton from '../../../public/assets/menu/Delete.svg';
import { useState } from 'react';
import DeleteConfirmModal from '../../widgets/host/DeleteConfirmModal';
import { useEventDeletion } from '../../entities/event/hook/useEventHook';

interface EventCardProps {
id: number;
Expand All @@ -16,6 +21,8 @@ interface EventCardProps {
hashtags: string[];
onClick?: () => void;
children?: React.ReactNode;
isDelete?: boolean;
onDeleteSuccess?: (eventId: number) => void;
}

const EventCard = ({
Expand All @@ -29,16 +36,20 @@ const EventCard = ({
hashtags,
onClick,
children,
isDelete = false,
onDeleteSuccess,
}: EventCardProps) => {
const navigate = useNavigate();
const { pathname } = useLocation();
const [isModalOpen, setIsModalOpen] = useState(false);
const { mutate } = useEventDeletion();

const isHostPage = pathname.startsWith(`/menu/myHost`) || pathname.startsWith(`/menu/hostDetail`);

return (
<div
onClick={onClick}
className="w-full max-w-full h-full min-h-[240px] md:min-h-[300px] max-h-full p-4 bg-white rounded-lg shadow-md cursor-pointer flex flex-col justify-between"
className="w-full max-w-full h-full min-h-[240px] md:min-h-[300px] max-h-full p-2 md:p-4 bg-white rounded-lg shadow-md cursor-pointer flex flex-col justify-between"
>
{/* 이미지 */}
<img src={img} alt={eventTitle} className="object-cover w-full rounded-md sm:h-20 md:h-24 lg:h-28" />
Expand Down Expand Up @@ -84,17 +95,47 @@ const EventCard = ({

{/* 대시보드 버튼 */}
{isHostPage && (
<TertiaryButton
label="호스트 대시보드 바로가기"
type="button"
color="pink"
size="small"
onClick={event => {
event?.stopPropagation();
navigate(`/dashboard/${id}`);
}}
className="w-31.5 md:w-33 mt-2"
/>
<div className="flex justify-between items-center h-7">
<TertiaryButton
label="호스트 대시보드 바로가기"
type="button"
color="pink"
size="small"
onClick={event => {
event?.stopPropagation();
navigate(`/dashboard/${id}`);
}}
className="w-31.5 md:w-33"
/>
{isDelete && (
<IconButton
iconPath={<img src={deleteButton} />}
size="small"
onClick={e => {
e.stopPropagation();
setIsModalOpen(true);
}}
iconClassName="w-5 h-5 md:w-7 md:h-7 bg-red-500 hover:bg-red-600 rounded-[5px] p-1"
/>
)}

<DeleteConfirmModal
isOpen={isModalOpen}
onClose={() => setIsModalOpen(false)}
onConfirm={() => {
mutate(id, {
onSuccess: () => {
onDeleteSuccess?.(id);
setIsModalOpen(false);
},
onError: () => {
alert('이벤트 삭제에 실패했습니다.');
setIsModalOpen(false);
},
});
}}
/>
</div>
)}
</div>
</div>
Expand Down
Loading