diff --git a/apps/web/src/app/tournament/[id]/create/_apis/postTournamentItemLink.ts b/apps/web/src/apis/postTournamentItemLink.ts similarity index 75% rename from apps/web/src/app/tournament/[id]/create/_apis/postTournamentItemLink.ts rename to apps/web/src/apis/postTournamentItemLink.ts index 5d098526..043a2786 100644 --- a/apps/web/src/app/tournament/[id]/create/_apis/postTournamentItemLink.ts +++ b/apps/web/src/apis/postTournamentItemLink.ts @@ -1,8 +1,8 @@ -import { clientApi } from '@/apis/client'; import { ENDPOINTS } from '@/consts/api'; import type { ApiResponseT } from '@/types/api'; +import type { PostTournamentItemLinkResponseT } from '@/types/tournament'; -import type { PostTournamentItemLinkResponseT } from '../_types/tournament'; +import { clientApi } from './client'; export const postTournamentItemLink = async (tournamentId: number, url: string) => { const { data } = await clientApi.post>( diff --git a/apps/web/src/app/tournament/[id]/_common/_hooks/useDeleteTournamentItem.ts b/apps/web/src/app/tournament/[id]/_common/_hooks/useDeleteTournamentItem.ts index 1824fc5e..5d006d3a 100644 --- a/apps/web/src/app/tournament/[id]/_common/_hooks/useDeleteTournamentItem.ts +++ b/apps/web/src/app/tournament/[id]/_common/_hooks/useDeleteTournamentItem.ts @@ -36,6 +36,7 @@ export const useDeleteTournamentItem = (tournamentId: number, tournamentItemId: /** 토너먼트가 시작된 경우 */ if (code === ERROR_CODE.TOURNAMENT_NOT_PENDING) { + toast.error(getApiErrorMessage(error)); queryClient.invalidateQueries({ queryKey: ['tournament', tournamentId] }); if (pathname !== ROUTES.TOURNAMENT_CREATE(tournamentId)) router.replace(ROUTES.TOURNAMENT_CREATE(tournamentId)); diff --git a/apps/web/src/app/tournament/[id]/create/_components/invite-friends/InviteFriendsDialog.tsx b/apps/web/src/app/tournament/[id]/create/_components/invite-friends/InviteFriendsDialog.tsx index 0f3bb745..d1a459eb 100644 --- a/apps/web/src/app/tournament/[id]/create/_components/invite-friends/InviteFriendsDialog.tsx +++ b/apps/web/src/app/tournament/[id]/create/_components/invite-friends/InviteFriendsDialog.tsx @@ -1,5 +1,6 @@ 'use client'; +import { ERROR_CODE, ERROR_MESSAGE_MAP } from '@piki/core'; import { useEffect, useMemo, useState } from 'react'; import { toast } from 'sonner'; @@ -103,7 +104,11 @@ function InviteFriendsDialog({ }; const handleSendInviteLink = async () => { - if (!inviteUrl) return; + /** 이미 시작된 토너먼트인 경우 */ + if (!inviteUrl) { + toast.error(ERROR_MESSAGE_MAP[ERROR_CODE.TOURNAMENT_NOT_PENDING]); + return; + } const result = await share({ title: 'piki 토너먼트 초대', diff --git a/apps/web/src/app/tournament/[id]/create/_hooks/usePatchInviteExpiry.ts b/apps/web/src/app/tournament/[id]/create/_hooks/usePatchInviteExpiry.ts index acce0ff7..21f5d0f7 100644 --- a/apps/web/src/app/tournament/[id]/create/_hooks/usePatchInviteExpiry.ts +++ b/apps/web/src/app/tournament/[id]/create/_hooks/usePatchInviteExpiry.ts @@ -31,6 +31,7 @@ export const usePatchInviteExpiry = (tournamentId: number) => { /** 토너먼트가 시작된 경우 */ if (code === ERROR_CODE.TOURNAMENT_NOT_PENDING) { + toast.error(getApiErrorMessage(error)); queryClient.invalidateQueries({ queryKey: ['tournament', tournamentId] }); return; } diff --git a/apps/web/src/app/tournament/[id]/create/_types/tournament.ts b/apps/web/src/app/tournament/[id]/create/_types/tournament.ts deleted file mode 100644 index b4175a82..00000000 --- a/apps/web/src/app/tournament/[id]/create/_types/tournament.ts +++ /dev/null @@ -1,3 +0,0 @@ -export type PostTournamentItemLinkResponseT = { - tournamentItemId: number; -}; diff --git a/apps/web/src/app/tournament/[id]/create/by-wish/_hooks/usePostTournamentItemsByWish.ts b/apps/web/src/app/tournament/[id]/create/by-wish/_hooks/usePostTournamentItemsByWish.ts index a59f2937..5c3085c3 100644 --- a/apps/web/src/app/tournament/[id]/create/by-wish/_hooks/usePostTournamentItemsByWish.ts +++ b/apps/web/src/app/tournament/[id]/create/by-wish/_hooks/usePostTournamentItemsByWish.ts @@ -4,6 +4,7 @@ import { useRouter } from 'next/navigation'; import { toast } from 'sonner'; import { PREV_ITEM_COUNT_KEY } from '@/app/tournament/[id]/create/_consts/tournamentItemBasket'; +import { QUERY_ACTION } from '@/consts/queryAction'; import { ROUTES } from '@/consts/route'; import { useBackWithFallback } from '@/hooks/useBackWithFallback'; import { getApiErrorCode, isGlobalNetError } from '@/utils/apiError'; @@ -33,13 +34,22 @@ export const usePostTournamentItemsByWish = (tournamentId: number, previousItemC const code = getApiErrorCode(error); - /** 토너먼트가 시작됐거나 삭제, 존재하지 않는 경우 */ - if (code === ERROR_CODE.TOURNAMENT_NOT_PENDING || code === ERROR_CODE.TOURNAMENT_NOT_FOUND) { + /** 토너먼트가 시작된 경우 */ + if (code === ERROR_CODE.TOURNAMENT_NOT_PENDING) { + toast.error(getApiErrorMessage(error)); queryClient.invalidateQueries({ queryKey: ['tournament', tournamentId] }); router.replace(ROUTES.TOURNAMENT_CREATE(tournamentId)); return; } + /** 토너먼트가 삭제된 경우 */ + if (code === ERROR_CODE.TOURNAMENT_NOT_FOUND) { + router.replace( + `${ROUTES.HOME}?${QUERY_ACTION.KEY}=${QUERY_ACTION.VALUE.TOURNAMENT_NOT_FOUND}` + ); + return; + } + /** * 400: 아이템 32개 초과 * 403: 토너먼트 참여 권한 없음·위시에 없는 아이템 diff --git a/apps/web/src/app/tournament/[id]/item/[itemId]/_hooks/usePatchTournamentItem.ts b/apps/web/src/app/tournament/[id]/item/[itemId]/_hooks/usePatchTournamentItem.ts index 92351cde..34abcd70 100644 --- a/apps/web/src/app/tournament/[id]/item/[itemId]/_hooks/usePatchTournamentItem.ts +++ b/apps/web/src/app/tournament/[id]/item/[itemId]/_hooks/usePatchTournamentItem.ts @@ -3,6 +3,7 @@ import { useMutation, useQueryClient } from '@tanstack/react-query'; import { useRouter } from 'next/navigation'; import { toast } from 'sonner'; +import { QUERY_ACTION } from '@/consts/queryAction'; import { ROUTES } from '@/consts/route'; import { useBackWithFallback } from '@/hooks/useBackWithFallback'; import type { PatchItemRequestT } from '@/types/item'; @@ -37,16 +38,22 @@ export const usePatchTournamentItem = (tournamentId: number, tournamentItemId: n const code = getApiErrorCode(error); - /** 토너먼트가 시작됐거나 삭제, 존재하지 않는 경우 */ - if ( - code === ERROR_CODE.TOURNAMENT_NOT_PENDING || - code === ERROR_CODE.TOURNAMENT_NOT_FOUND - ) { + /** 토너먼트가 시작된 경우 */ + if (code === ERROR_CODE.TOURNAMENT_NOT_PENDING) { + toast.error(getApiErrorMessage(error)); queryClient.invalidateQueries({ queryKey: ['tournament', tournamentId] }); router.replace(ROUTES.TOURNAMENT_CREATE(tournamentId)); return; } + /** 토너먼트가 삭제된 경우 */ + if (code === ERROR_CODE.TOURNAMENT_NOT_FOUND) { + router.replace( + `${ROUTES.HOME}?${QUERY_ACTION.KEY}=${QUERY_ACTION.VALUE.TOURNAMENT_NOT_FOUND}` + ); + return; + } + /** * 400: 상품 이름·가격 미입력 * 403: 토너먼트 참여 권한 없음 diff --git a/apps/web/src/components/get-item-dialog/ByLinkDialog.tsx b/apps/web/src/components/get-item-dialog/ByLinkDialog.tsx index 2117ec69..20ee6099 100644 --- a/apps/web/src/components/get-item-dialog/ByLinkDialog.tsx +++ b/apps/web/src/components/get-item-dialog/ByLinkDialog.tsx @@ -3,11 +3,11 @@ import { useParams } from 'next/navigation'; import { useState } from 'react'; -import { usePostTournamentItemLink } from '@/app/tournament/[id]/create/_hooks/usePostTournamentItemLink'; import { LinkIconFill } from '@/assets/icons'; import Button from '@/components/button'; import { Dialog, DialogContent, DialogDescription, DialogTitle } from '@/components/dialog'; import Input from '@/components/input'; +import { usePostTournamentItemLink } from '@/hooks/usePostTournamentItemLink'; import { usePostWishLink } from '@/hooks/usePostWishLink'; import type { ItemTypeT } from '@/types/item'; import { isGlobalNetError } from '@/utils/apiError'; diff --git a/apps/web/src/app/tournament/[id]/create/_hooks/usePostTournamentItemLink.ts b/apps/web/src/hooks/usePostTournamentItemLink.ts similarity index 93% rename from apps/web/src/app/tournament/[id]/create/_hooks/usePostTournamentItemLink.ts rename to apps/web/src/hooks/usePostTournamentItemLink.ts index 155279f8..80091343 100644 --- a/apps/web/src/app/tournament/[id]/create/_hooks/usePostTournamentItemLink.ts +++ b/apps/web/src/hooks/usePostTournamentItemLink.ts @@ -3,13 +3,12 @@ import { useMutation, useQueryClient } from '@tanstack/react-query'; import { useRouter } from 'next/navigation'; import { toast } from 'sonner'; +import { postTournamentItemLink } from '@/apis/postTournamentItemLink'; import { QUERY_ACTION } from '@/consts/queryAction'; import { ROUTES } from '@/consts/route'; import { getApiErrorCode, getApiErrorStatus, isGlobalNetError } from '@/utils/apiError'; import { getApiErrorMessage } from '@/utils/getApiErrorMessage'; -import { postTournamentItemLink } from '../_apis/postTournamentItemLink'; - type UsePostTournamentItemLinkOptionsT = { /** 입력 폼처럼 에러를 화면 안에서 안내하는 경우 false — 4xx 토스트를 끈다 */ showErrorToast?: boolean; @@ -35,6 +34,7 @@ export const usePostTournamentItemLink = ( /** 토너먼트가 시작된 경우 */ if (code === ERROR_CODE.TOURNAMENT_NOT_PENDING) { + if (showErrorToast) toast.error(getApiErrorMessage(error)); queryClient.invalidateQueries({ queryKey: ['tournament', tournamentId] }); return; } diff --git a/apps/web/src/hooks/usePostTournamentOCR.ts b/apps/web/src/hooks/usePostTournamentOCR.ts index bc932702..b16a7e4c 100644 --- a/apps/web/src/hooks/usePostTournamentOCR.ts +++ b/apps/web/src/hooks/usePostTournamentOCR.ts @@ -29,6 +29,7 @@ export const usePostTournamentOCR = (tournamentId: number) => { /** 토너먼트가 시작된 경우 */ if (code === ERROR_CODE.TOURNAMENT_NOT_PENDING) { + toast.error(getApiErrorMessage(error)); queryClient.invalidateQueries({ queryKey: ['tournament', tournamentId] }); return; } diff --git a/apps/web/src/types/tournament.ts b/apps/web/src/types/tournament.ts index d1e9333a..8c158bc4 100644 --- a/apps/web/src/types/tournament.ts +++ b/apps/web/src/types/tournament.ts @@ -56,6 +56,10 @@ export type PostCreateTournamentResponseT = { tournamentId: number; }; +export type PostTournamentItemLinkResponseT = { + tournamentItemId: number; +}; + /** 초대 코드 / 토너먼트 미리보기 응답 */ export type GetInvitePreviewResponseT = { tournamentId: number;