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
10 changes: 8 additions & 2 deletions design-system/ui/buttons/TertiaryButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@ interface TertiaryButtonProps {
type: 'button' | 'submit';
color: 'pink' | 'black';
size: 'small' | 'medium' | 'large';
disabled?: boolean;
onClick?: React.MouseEventHandler<HTMLButtonElement>;
className?: string;
}

const TertiaryButton = ({ label, type, color, size, onClick, className }: TertiaryButtonProps) => {
const TertiaryButton = ({ label, type, color, size, disabled, onClick, className }: TertiaryButtonProps) => {
const baseStyle = `flex justify-center items-center border rounded`;

const sizeClasses = {
Expand All @@ -23,7 +24,12 @@ const TertiaryButton = ({ label, type, color, size, onClick, className }: Tertia
: 'border-black text-black hover:bg-black hover:text-white hover:font-bold';

return (
<button type={type} className={`${baseStyle} ${sizeClasses[size]} ${colorStyle} ${className}`} onClick={onClick}>
<button
type={type}
disabled={disabled}
className={`${baseStyle} ${sizeClasses[size]} ${colorStyle} ${className}`}
onClick={onClick}
>
{label}
</button>
);
Expand Down
25 changes: 10 additions & 15 deletions src/app/provider/authStore.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { create } from 'zustand';
import { persist } from 'zustand/middleware';

interface AuthStore {
isLoggedIn: boolean;
Expand All @@ -14,22 +13,18 @@ interface AuthStore {
closeModal: () => void;
}

export const useAuthStore = create<AuthStore>()(
persist(
(set) => ({
isLoggedIn: false,
isModalOpen: false,
export const useAuthStore = create<AuthStore>()(set => ({
isLoggedIn: false,
isModalOpen: false,

openModal: () => set({ isModalOpen: true }),
closeModal: () => set({ isModalOpen: false }),
openModal: () => set({ isModalOpen: true }),
closeModal: () => set({ isModalOpen: false }),

login: () => set({ isLoggedIn: true }),
logout: () => set({ isLoggedIn: false, name: null }),
login: () => set({ isLoggedIn: true }),
logout: () => set({ isLoggedIn: false, name: null }),

name: null,
setName: (name) => set({ name }),
}), { name: 'auth-storage' }
)
);
name: null,
setName: name => set({ name }),
}));

export default useAuthStore;
18 changes: 14 additions & 4 deletions src/entities/event/api/event.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +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 },
});
return response.data;
};
Expand All @@ -23,7 +24,9 @@ export const getAllEventsInfinite = async ({
params.append('page', page.toString());
params.append('size', size.toString());

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

const items = response.data.result ?? [];

Expand All @@ -45,7 +48,9 @@ export const getCategoryEventsInfinite = async ({
params.append('page', page.toString());
params.append('size', size.toString());

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

const items = response.data.result ?? [];

Expand All @@ -57,7 +62,9 @@ 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}`);
const response = await axiosClient.get<{ result: EventItem[] }>(`/events?tags=${tag}&page=${page}&size=${size}`, {
headers: { skipAuth: true },
});
return response.data.result || [];
};

Expand All @@ -67,7 +74,10 @@ export const getEventByCategory = async (
{ page, size }: PaginationParams
): Promise<EventItem[]> => {
const response = await axiosClient.get<EventItem[]>(
`/events/category?category=${category}&page=${page}&size=${size}`
`/events/category?category=${category}&page=${page}&size=${size}`,
{
headers: { skipAuth: true },
}
);
return response.data;
};
Expand Down
7 changes: 5 additions & 2 deletions src/entities/event/hook/useEventHook.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,20 @@ 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';
import useAuthStore from '../../../app/provider/authStore';

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

const eventId = Number(id);

const { data } = useQuery({
queryKey: ['eventDetail', eventId],
queryFn: () => eventDetail({ eventId, userId: user?.id }),
enabled: !!user?.id && !!eventId,
enabled: !!eventId,
retry: false,
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

쿼리 재시도 비활성화에 대한 검토 필요

retry: false 설정은 일시적인 네트워크 오류나 서버 문제 발생 시 자동 재시도를 하지 않아 사용자 경험에 부정적인 영향을 줄 수 있습니다. 이벤트 상세 정보는 중요한 데이터이므로 재시도 로직을 유지하는 것을 고려해보세요.

-    retry: false,
+    retry: 2, // 또는 기본값 사용을 위해 이 라인 제거
📝 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
retry: false,
- retry: false,
+ retry: 2, // 또는 기본값 사용을 위해 이 라인 제거
🤖 Prompt for AI Agents
In src/entities/event/hook/useEventHook.ts at line 19, the retry option is set
to false, disabling automatic retries on query failure. Since event details are
important, review this setting and consider enabling retry to handle transient
network or server errors gracefully. Adjust the retry configuration to allow a
reasonable number of retries instead of disabling it completely.

});

return { data };
Expand Down
22 changes: 13 additions & 9 deletions src/features/join/api/user.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
import { axiosClient } from "../../../shared/types/api/http-client"
import { UserInfoRequest, UserInfoResponse } from "../model/userInformation";
import { axiosClient } from '../../../shared/types/api/http-client';
import { UserInfoRequest, UserInfoResponse } from '../model/userInformation';

export const readUser = async (): Promise<UserInfoResponse> => {
const response = await axiosClient.get<{ result: UserInfoResponse }>('/users');
return response.data.result;
}
const response = await axiosClient.get<{ result: UserInfoResponse }>('/users', {
headers: { skipAuth: true },
});
return response.data.result;
};

export const updateUser = async(data: UserInfoRequest): Promise<UserInfoResponse> => {
const response = await axiosClient.put<UserInfoResponse>('/users', data);
return response.data;
}
export const updateUser = async (data: UserInfoRequest): Promise<UserInfoResponse> => {
const response = await axiosClient.put<UserInfoResponse>('/users', data, {
headers: { skipAuth: true },
});
return response.data;
};
19 changes: 10 additions & 9 deletions src/features/join/hooks/useUserHook.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,16 @@ import { useMutation, useQuery } from '@tanstack/react-query';
import { readUser, updateUser } from '../api/user';
import { UserInfoRequest, UserInfoResponse } from '../model/userInformation';

export const useUserInfo = () => {
return useQuery<UserInfoResponse>({
queryKey: ['userInfo'],
queryFn: readUser,
});
export const useUserInfo = (enabled: boolean = true) => {
return useQuery<UserInfoResponse>({
queryKey: ['userInfo'],
queryFn: readUser,
enabled,
});
};

export const useUserUpdate = () => {
return useMutation<UserInfoResponse, Error, UserInfoRequest>({
mutationFn: updateUser,
});
}
return useMutation<UserInfoResponse, Error, UserInfoRequest>({
mutationFn: updateUser,
});
};
8 changes: 7 additions & 1 deletion src/pages/bookmark/ui/BookmarkPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@ import searchIcon from '../../../../design-system/icons/Search.svg';
import BottomBar from '../../../widgets/main/ui/BottomBar';
import EventCard from '../../../shared/ui/EventCard';
import { useBookmarks } from '../../../features/bookmark/hook/useBookmarkHook';
import useAuthStore from '../../../app/provider/authStore';

const BookmarkPage = () => {
const navigate = useNavigate();
const { data } = useBookmarks();
const isLoggedIn = useAuthStore(state => state.isLoggedIn);

return (
<div className="relative">
Expand All @@ -20,7 +22,11 @@ const BookmarkPage = () => {
}
/>
<div className="grid grid-cols-2 gap-4 mx-5 mt-3 md:grid-cols-2 lg:grid-cols-2 z-50">
{data?.length ? (
{!isLoggedIn ? (
<p className="col-span-2 mt-20 text-center text-sm md:text-base text-red-500">
로그인이 필요한 서비스입니다.
</p>
) : data?.length ? (
data.map(event => (
<EventCard
id={event.id}
Expand Down
10 changes: 9 additions & 1 deletion src/pages/event/ui/host/HostSelectionPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import useHostChannelList from '../../../../entities/host/hook/useHostChannelLis
import IconButton from '../../../../../design-system/ui/buttons/IconButton';
import CloseButton from '../../../../../public/assets/event-manage/creation/CloseBtn.svg';
import { useHostDeletion } from '../../../../features/host/hook/useHostHook';
import useAuthStore from '../../../../app/provider/authStore';

interface HostSelectionPageProps {
onNext: (nextStep: string) => void;
Expand All @@ -17,6 +18,7 @@ const HostSelectionPage = ({ onNext, currentStep, onValidationChange }: HostSele
const [selected, setSelected] = useState<number | null>(null);
const { data, refetch } = useHostChannelList();
const { mutate: deleteHost } = useHostDeletion();
const isLoggedIn = useAuthStore(state => state.isLoggedIn);

const handleHostClick = (host: { id: number; hostChannelName: string; profileImageUrl: string }) => {
setSelected(host.id);
Expand Down Expand Up @@ -49,7 +51,13 @@ const HostSelectionPage = ({ onNext, currentStep, onValidationChange }: HostSele
return (
<div className="flex flex-col w-full px-2">
<div
onClick={() => onNext(String(currentStep + 1))}
onClick={() => {
if (!isLoggedIn) {
alert('로그인이 필요한 서비스입니다.');
return;
}
onNext(String(currentStep + 1));
}}
className="flex justify-start items-center px-3 py-4 cursor-pointer"
>
<button className="flex justify-center items-center w-12 h-12 md:w-14 md:h-14 bg-gray2 rounded-full">
Expand Down
22 changes: 18 additions & 4 deletions src/pages/menu/ui/MyPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,14 @@ import { useUserInfo, useUserUpdate } from '../../../features/join/hooks/useUser
import useAuthStore from '../../../app/provider/authStore';

const MyPage = () => {
const { data, isLoading, error } = useUserInfo();
const { mutate: updateUser } = useUserUpdate();
const { setName } = useAuthStore();
const isLoggedIn = useAuthStore(state => state.isLoggedIn);

const { data, isLoading, error } = useUserInfo(isLoggedIn);
const { mutate: updateUser } = useUserUpdate();

const [isChanged, setIsChanged] = useState<string>('');

const {
register,
handleSubmit,
Expand Down Expand Up @@ -74,7 +77,7 @@ const MyPage = () => {
<h1 className="text-xl font-bold">기본 정보</h1>
<div className="flex flex-col">
<span className="text-sm md:text-base font-normal">이메일</span>
<span className="text-sm md:text-base font-light">{data?.email}</span>
<span className="text-sm md:text-base font-light">{data?.email || '로그인이 필요합니다'}</span>
</div>
</div>
<form onSubmit={handleSubmit(onSubmit)} className="flex flex-col gap-2">
Expand All @@ -85,6 +88,7 @@ const MyPage = () => {
placeholder={data?.name}
className=" h-8"
labelClassName="text-sm md:text-base font-normal"
disabled={!isLoggedIn}
{...register('name')}
/>
{errors.name && <span className=" text-sm text-red-500 whitespace-nowrap">{errors.name.message}</span>}
Expand All @@ -95,14 +99,24 @@ const MyPage = () => {
placeholder={data?.phoneNumber}
className="h-8"
labelClassName="text-sm md:text-base font-normal"
disabled={!isLoggedIn}
{...register('phone')}
/>
{errors.phone && <span className="text-sm text-red-500 whitespace-nowrap">{errors.phone.message}</span>}
</div>
</div>
{isChanged && <span className="text-red-500 text-sm">{isChanged}</span>}
<TertiaryButton label="저장하기" color="black" size="large" type="submit" className="w-24 h-8" />
<TertiaryButton
label="저장하기"
color="black"
size="large"
type="submit"
className="w-24 h-8"
disabled={!isLoggedIn}
/>
</form>

{!isLoggedIn && <p className="text-sm text-gray-500 mt-2">로그인 후 정보를 수정하실 수 있습니다.</p>}
</div>
{/* <PaymentCard title={'등록된 카드'} /> */}
<BottomBar />
Expand Down
36 changes: 22 additions & 14 deletions src/pages/menu/ui/MyTicketPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { useCancelTicket, useTicketOrders } from '../../../features/ticket/hooks
import { OrderTicketResponse } from '../../../features/ticket/model/Order';
import EmailDeleteModal from '../../../widgets/dashboard/ui/email/EmailDeleteModal';
import TertiaryButton from '../../../../design-system/ui/buttons/TertiaryButton';
import useAuthStore from '../../../app/provider/authStore';

const MyTicketPage = () => {
const [isModalOpen, setIsModalOpen] = useState(false);
Expand All @@ -22,6 +23,7 @@ const MyTicketPage = () => {

const { data, isLoading, isError } = useTicketOrders(0, 10);
const { mutate: cancelTicket } = useCancelTicket();
const isLoggedIn = useAuthStore(state => state.isLoggedIn);

const handleCancelButtonClick = () => {
if (isCancelMode) {
Expand All @@ -48,6 +50,15 @@ const MyTicketPage = () => {
}
};

const handleEventCardClick = (ticket: OrderTicketResponse) => {
if (isCancelMode) {
setSelectedIds(prev => (prev.includes(ticket.id) ? prev.filter(id => id !== ticket.id) : [...prev, ticket.id]));
} else {
setSelectedTicket(ticket);
setIsModalOpen(true);
}
};

useEffect(() => {
if (data?.result) {
setTickets(data.result);
Expand All @@ -56,7 +67,7 @@ const MyTicketPage = () => {

return (
<TicketHostLayout image={TicketLogo} centerContent="내 티켓" ticketPage={true} isCancelMode={isCancelMode}>
{!isModalOpen && (
{!isModalOpen && tickets.length > 0 && (
<div className="flex justify-end mx-6 mt-24">
<TertiaryButton
label={isCancelMode ? '선택 완료' : '티켓 취소'}
Expand All @@ -70,10 +81,16 @@ const MyTicketPage = () => {

{/* 이벤트 카드 목록 */}
<div className="grid grid-cols-2 gap-4 mx-6 mt-2 md:grid-cols-2 lg:grid-cols-2 pb-4">
{isLoading ? (
<p className="col-span-2 text-center text-sm md:text-base">티켓을 불러오는 중입니다...</p>
{!isLoggedIn ? (
<p className="col-span-2 mt-28 text-center text-sm md:text-base text-red-500">
로그인이 필요한 서비스입니다.
</p>
) : isLoading ? (
<p className="col-span-2 mt-28 text-center text-sm md:text-base">티켓을 불러오는 중입니다...</p>
) : isError ? (
<p className="col-span-2 text-center text-sm md:text-base text-red-500">티켓을 불러오는데 실패했습니다.</p>
<p className="col-span-2 mt-28 text-center text-sm md:text-base text-red-500">
티켓을 불러오는데 실패했습니다.
</p>
) : tickets.length > 0 ? (
tickets.map(ticket => (
<EventCard
Expand All @@ -86,16 +103,7 @@ const MyTicketPage = () => {
eventDate={ticket.event.startDate}
location={ticket.event.address}
hashtags={ticket.event.hashtags}
onClick={() => {
if (isCancelMode) {
setSelectedIds(prev =>
prev.includes(ticket.id) ? prev.filter(id => id !== ticket.id) : [...prev, ticket.id]
);
} else {
setSelectedTicket(ticket);
setIsModalOpen(true);
}
}}
onClick={() => handleEventCardClick(ticket)}
className={`transition-transform duration-200 ${
isCancelMode && selectedIds.includes(ticket.id) ? 'scale-95 border-2 border-pink-400' : ''
}`}
Expand Down
Loading