-
Notifications
You must be signed in to change notification settings - Fork 0
feat: 대시보드 이벤트 정보 API 연동 #108
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
3b1cded
fe8f92b
0b9cdcd
123c524
b70b013
0f0d506
cb738ca
cb70269
1883471
c48879d
139d739
c9b39f8
54fece1
ad7a285
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| import { axiosClient } from '../../../shared/types/api/http-client'; | ||
| import { EventDetailRequest } from '../model/event'; | ||
|
|
||
| export const eventDetail = async (dto: EventDetailRequest) => { | ||
| const response = await axiosClient.get(`/events/${dto.eventId}`); | ||
| return response.data; | ||
| }; |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,17 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { useQuery } from '@tanstack/react-query'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { eventDetail } from '../api/event'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { useParams } from 'react-router-dom'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const useEventDetail = () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const { id } = useParams(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const eventId = Number(id); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const { data } = useQuery({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| queryKey: ['eventDetail', eventId], | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| queryFn: () => eventDetail({ eventId }), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return { data }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+1
to
+16
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion 이벤트 상세 정보를 가져오는 훅이 구현되었습니다. 몇 가지 개선 사항을 제안합니다. 이벤트 상세 정보를 가져오는 중앙화된 훅의 구현은 잘 되었으나, 다음과 같은 개선 사항을 고려해 보세요:
아래는 개선된 구현을 제안합니다: import { useQuery } from '@tanstack/react-query';
import { eventDetail } from '../api/event';
import { useParams } from 'react-router-dom';
+ import { EventDetailResponse } from '../model/event';
- const useEventDetail = () => {
+ const useEventDetail = () => {
const { id } = useParams();
+ // id가 없거나 유효한 숫자가 아닌 경우 처리
+ if (!id || isNaN(Number(id))) {
+ console.error('Invalid event ID');
+ return { data: undefined, isLoading: false, error: new Error('Invalid event ID') };
+ }
const eventId = Number(id);
- const { data } = useQuery({
+ const { data, isLoading, error } = useQuery<EventDetailResponse, Error>({
queryKey: ['eventDetail', eventId],
queryFn: () => eventDetail({ eventId }),
});
- return { data };
+ return { data, isLoading, error };
};
export default useEventDetail;📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| export default useEventDetail; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| import { BaseEvent } from '../../../shared/types/baseEventType'; | ||
|
|
||
| export interface EventDetailRequest { | ||
| eventId: number; | ||
| } | ||
|
|
||
| export interface EventDetailResponse { | ||
| result: BaseEvent & { | ||
| id: number; | ||
| participantCount: number; | ||
| hostChannelName: string; | ||
| hostChannelDescription: string; | ||
| status: string; | ||
| }; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,10 +1,16 @@ | ||
| import { axiosClient } from '../../../shared/types/api/http-client'; | ||
| import { UpdateEventRequest } from '../model/event'; | ||
|
|
||
| export const getEventInfo = async (eventId: string) => { | ||
| export const getHostDashboard = async (eventId: number) => { | ||
| const response = await axiosClient.get('/host-channels/dashboard', { | ||
| params: { | ||
| eventId: eventId, | ||
| }, | ||
| }); | ||
| return response.data.result; | ||
| }; | ||
|
|
||
| export const updateEventInfo = async (eventId: number, dto: Partial<UpdateEventRequest>) => { | ||
| const response = await axiosClient.put(`/events/${eventId}`, dto); | ||
| return response.data; | ||
| }; |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,29 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { useMutation, useQuery } from '@tanstack/react-query'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { useParams } from 'react-router-dom'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { getHostDashboard, updateEventInfo } from '../api/event'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { dashboardData } from '../../../shared/types/dashboardType'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { UpdateEventRequest } from '../model/event'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| export const useGetEventHook = () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const { id } = useParams(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const eventId = Number(id); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const { data: eventInfo } = useQuery<dashboardData>({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| queryKey: ['eventInfo', eventId], | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| queryFn: () => getHostDashboard(eventId), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return { eventInfo }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+7
to
+18
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion 캐시 설정과 오류 처리 개선 필요
export const useGetEventHook = () => {
const { id } = useParams();
const eventId = Number(id);
- const { data: eventInfo } = useQuery<dashboardData>({
+ const { data: eventInfo, error, isLoading } = useQuery<dashboardData>({
queryKey: ['eventInfo', eventId],
queryFn: () => getHostDashboard(eventId),
+ staleTime: 5 * 60 * 1000, // 5분 동안 데이터를 '신선'하게 유지
+ retry: 1, // 실패 시 1번만 재시도
+ refetchOnWindowFocus: true, // 창이 포커스를 받을 때 데이터 새로고침
});
- return { eventInfo };
+ return { eventInfo, error, isLoading };
};📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| export const useUpdateEventHook = () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const { id } = useParams(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const eventId = Number(id); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const mutation = useMutation({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| mutationFn: (dto: Partial<UpdateEventRequest>) => updateEventInfo(eventId, dto), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return mutation; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
This file was deleted.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| import { BaseEvent } from '../../../shared/types/baseEventType'; | ||
|
|
||
| export interface UpdateEventRequest extends BaseEvent { | ||
| hostChannelId: number; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,18 +1,5 @@ | ||
| export interface CreateEventRequest { | ||
| import { BaseEvent } from '../../../../shared/types/baseEventType'; | ||
|
|
||
| export interface CreateEventRequest extends BaseEvent { | ||
| hostChannelId: number; | ||
| title: string; | ||
| startDate: string; | ||
| endDate: string; | ||
| startTime: string; | ||
| endTime: string; | ||
| bannerImageUrl: string; | ||
| description: string; | ||
| referenceLinks: { address: string; detailAddress: string; title: string; url: string }[]; | ||
| onlineType: 'ONLINE' | 'OFFLINE'; | ||
| address: string; | ||
| location: { lat: number; lng: number }; | ||
| category: string; | ||
| hashtags: string[]; | ||
| organizerEmail: string; | ||
| organizerPhoneNumber: string; | ||
| } |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -1,64 +1,62 @@ | ||||||
| import { useState } from 'react'; | ||||||
| import { useEffect, useState } from 'react'; | ||||||
| import { FunnelState } from '../model/FunnelContext'; | ||||||
| import AddButton from '../../../../../public/assets/event-manage/creation/AddBtn.svg'; | ||||||
| import CloseButton from '../../../../../public/assets/event-manage/creation/CloseBtn.svg'; | ||||||
| import Link from '../../../../../public/assets/event-manage/creation/Link.svg'; | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 'Link' 명칭 충돌 해결 필요
- import Link from '../../../../../public/assets/event-manage/creation/Link.svg';
+ import LinkIcon from '../../../../../public/assets/event-manage/creation/Link.svg';
export interface Link {
title: string;
url: string;
address: string;
detailAddress: string;
}그리고 JSX에서도 아이콘 사용 부분을 변경해야 합니다: - <img src={Link} alt="링크 이미지" className="p-2" />
+ <img src={LinkIcon} alt="링크 이미지" className="p-2" />Also applies to: 13-18 |
||||||
| import { FunnelState } from '../model/FunnelContext'; | ||||||
|
|
||||||
| interface LinkInputProps { | ||||||
| eventState?: FunnelState['eventState']; | ||||||
| value?: Link[]; | ||||||
| onChange?: (links: Link[]) => void; | ||||||
| setEventState?: React.Dispatch<React.SetStateAction<FunnelState['eventState']>>; | ||||||
| } | ||||||
|
|
||||||
| const LinkInput = ({ eventState, setEventState }: LinkInputProps) => { | ||||||
| const [links, setLinks] = useState<{ title: string; url: string; address: string; detailAddress: string }[]>( | ||||||
| eventState?.referenceLinks || [] | ||||||
| ); | ||||||
| export interface Link { | ||||||
| title: string; | ||||||
| url: string; | ||||||
| address: string; | ||||||
| detailAddress: string; | ||||||
| } | ||||||
|
|
||||||
| const LinkInput = ({ value, onChange, setEventState }: LinkInputProps) => { | ||||||
| const [links, setLinks] = useState<Link[]>([]); | ||||||
| const [activeInput, setActiveInput] = useState<{ field: 'title' | 'url' | null }>({ | ||||||
| field: null, | ||||||
| }); | ||||||
| const [hoveredInput, setHoveredInput] = useState<{ field: 'title' | 'url' | null }>({ | ||||||
| field: null, | ||||||
| }); | ||||||
|
|
||||||
| const updateAll = (newLinks: Link[]) => { | ||||||
| setLinks(newLinks); | ||||||
| onChange?.(newLinks); | ||||||
| setEventState?.(prev => ({ ...prev, referenceLinks: newLinks })); | ||||||
| }; | ||||||
|
|
||||||
| const addNewLink = () => { | ||||||
| const newLink = { | ||||||
| title: '', | ||||||
| url: '', | ||||||
| address: '', | ||||||
| detailAddress: '', | ||||||
| }; | ||||||
| setLinks([...links, newLink]); | ||||||
| setActiveInput({ field: null }); | ||||||
| if (setEventState) { | ||||||
| setEventState(prev => ({ | ||||||
| ...prev, | ||||||
| referenceLinks: [...prev.referenceLinks, newLink], | ||||||
| })); | ||||||
| } | ||||||
| updateAll([...links, newLink]); | ||||||
| }; | ||||||
|
|
||||||
| const removeLink = (title: string) => { | ||||||
| const updatedLinks = links.filter(link => link.title !== title); | ||||||
| setLinks(updatedLinks); | ||||||
| if (setEventState) { | ||||||
| setEventState(prev => ({ | ||||||
| ...prev, | ||||||
| referenceLinks: updatedLinks, | ||||||
| })); | ||||||
| } | ||||||
| const removeLink = (index: number) => { | ||||||
| const newLinks = links.filter((_, i) => i !== index); | ||||||
| updateAll(newLinks); | ||||||
| }; | ||||||
|
|
||||||
| const updateLink = (title: string, field: 'url' | 'title', value: string) => { | ||||||
| const updatedLinks = links.map(link => (link.title === title ? { ...link, [field]: value } : link)); | ||||||
| setLinks(updatedLinks); | ||||||
| if (setEventState) { | ||||||
| setEventState(prev => ({ | ||||||
| ...prev, | ||||||
| referenceLinks: updatedLinks, | ||||||
| })); | ||||||
| } | ||||||
| const updateLink = (index: number, field: keyof Link, value: string) => { | ||||||
| const newLinks = [...links]; | ||||||
| newLinks[index] = { ...newLinks[index], [field]: value }; | ||||||
| updateAll(newLinks); | ||||||
| }; | ||||||
|
|
||||||
| useEffect(() => { | ||||||
| setLinks(value ?? []); | ||||||
| }, [value]); | ||||||
|
|
||||||
| return ( | ||||||
| <div className="flex flex-col gap-1"> | ||||||
| <h1 className="font-bold text-black text-lg">관련 링크</h1> | ||||||
|
|
@@ -77,7 +75,7 @@ const LinkInput = ({ eventState, setEventState }: LinkInputProps) => { | |||||
| <input | ||||||
| type="text" | ||||||
| value={link.title} | ||||||
| onChange={e => updateLink(link.title, 'title', e.target.value)} | ||||||
| onChange={e => updateLink(index, 'title', e.target.value)} | ||||||
| className="w-full min-w-[3rem] md:min-w-[6rem] h-8 text-placeholderText ml-1 outline-none bg-transparent text-sm md:text-base" | ||||||
| placeholder="참조링크" | ||||||
| autoFocus={activeInput.field === link.title && activeInput.field === 'title'} | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. autoFocus 로직 오류 수정 필요 현재 - autoFocus={activeInput.field === link.title && activeInput.field === 'title'}
+ autoFocus={activeInput.field === 'title'}📝 Committable suggestion
Suggested change
|
||||||
|
|
@@ -94,7 +92,7 @@ const LinkInput = ({ eventState, setEventState }: LinkInputProps) => { | |||||
| <input | ||||||
| type="text" | ||||||
| value={link.url} | ||||||
| onChange={e => updateLink(link.title, 'url', e.target.value)} | ||||||
| onChange={e => updateLink(index, 'url', e.target.value)} | ||||||
| className="w-full min-w-[10rem] md:min-w-[15rem] h-8 text-placeholderText ml-2 outline-none bg-transparent text-sm md:text-base" | ||||||
| placeholder="URL을 입력하세요" | ||||||
| autoFocus={activeInput.field === 'url'} | ||||||
|
|
@@ -104,7 +102,7 @@ const LinkInput = ({ eventState, setEventState }: LinkInputProps) => { | |||||
| <button | ||||||
| onClick={e => { | ||||||
| e.stopPropagation(); | ||||||
| removeLink(link.title); | ||||||
| removeLink(index); | ||||||
| }} | ||||||
| className="absolute top-1/2 right-2 transform -translate-y-1/2" | ||||||
| > | ||||||
|
|
||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
빈 options 배열에 대한 방어 코드가 필요합니다.
현재 코드는
options[0]의 값을 초기 선택값으로 사용하고 있는데, options 배열이 비어있을 경우 undefined 참조 오류가 발생할 수 있습니다. 아래와 같이 방어 코드를 추가하는 것이 좋습니다.📝 Committable suggestion