Skip to content
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

[Fix] 어드민 오류 수정 #185

Merged
merged 8 commits into from
Aug 22, 2024
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
13 changes: 13 additions & 0 deletions admin/src/apis/lotteryAPI.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {
DeleteLotteryWinnerResponse,
GetLotteryExpectationsParams,
GetLotteryExpectationsResponse,
GetLotteryParticipantResponse,
Expand Down Expand Up @@ -55,6 +56,18 @@ export const LotteryAPI = {
throw error;
}
},
async deleteLotteryWinner(token: string): Promise<DeleteLotteryWinnerResponse> {
try {
const response = await fetchWithTimeout(`${baseURL}/winner`, {
method: "DELETE",
headers: { ...headers, Authorization: `Bearer ${token}` },
});
return response.json();
} catch (error) {
console.error("Error:", error);
throw error;
}
},
async getLotteryParticipant(
{ size, page, phoneNumber }: GetLotteryWinnerParams,
token: string
Expand Down
21 changes: 21 additions & 0 deletions admin/src/components/Suspense/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { PropsWithChildren } from "react";

interface SuspenseProps extends PropsWithChildren {
isLoading?: boolean;
}

export default function Suspense({ children, isLoading = false }: SuspenseProps) {
return (
<>
{isLoading ? (
<div className="fixed z-10 h-screen w-full bg-n-neutral-950 flex flex-col justify-center items-center">
<h3 className="h-heading-3-bold text-n-white mb-20">
데이터를 불러오는 중입니다...
</h3>
</div>
) : (
children
)}
</>
);
}
4 changes: 3 additions & 1 deletion admin/src/components/TimePicker/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ export default function TimePicker({ time, disabled = false, onChangeTime }: Tim
* 시간-분 까지만 선택 가능
* 초는 0초를 디폴트로 넣는다
*/
const time = `${e.target.value}:00`;
const value = e.target.value;
const isMinuteEnd = value.split(":").length === 2;
const time = `${e.target.value}${isMinuteEnd ? `:00` : ""}`;
onChangeTime(time);
};

Expand Down
5 changes: 5 additions & 0 deletions admin/src/constants/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,8 @@ export const STATUS_MAP = {
[EVENT_STATUS.DURING]: "활성화",
[EVENT_STATUS.AFTER]: "종료",
};

export const ERROR_MAP = {
CONFLICT: "409",
NOT_FOUND: "404",
} as const;
12 changes: 10 additions & 2 deletions admin/src/hooks/useFetch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,27 +10,35 @@ export default function useFetch<T, P = void>(
const { showBoundary } = useErrorBoundary();

const [data, setData] = useState<T | null>(null);
const [isLoading, setIsLoading] = useState<boolean>(false);
const [isSuccess, setIsSuccess] = useState<boolean>(false);
const [isError, setIsError] = useState<boolean>(false);
const [errorStatus, setErrorStatus] = useState<string | null>(null);

const [cookies] = useCookies([COOKIE_KEY.ACCESS_TOKEN]);

const fetchData = async (params?: P) => {
setIsError(false);
setIsSuccess(false);
setIsLoading(true);
setErrorStatus(null);

try {
const data = await fetch(params as P, cookies[COOKIE_KEY.ACCESS_TOKEN]);
setData(data);
setIsSuccess(!!data);
} catch (error) {
setIsError(true);
console.error(error);
if (error instanceof Error) {
setErrorStatus(error.message);
}
if (showError) {
showBoundary(error);
}
} finally {
setIsLoading(false);
}
};

return { data, isSuccess, isError, fetchData };
return { data, isSuccess, isLoading, isError, errorStatus, fetchData };
}
14 changes: 13 additions & 1 deletion admin/src/hooks/useInfiniteFetch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ interface UseInfiniteFetchProps<R> {
initialPageParam?: number;
getNextPageParam: (currentPageParam: number, lastPage: R) => number | undefined;
startFetching?: boolean;
showError?: boolean;
}

interface InfiniteScrollData<T> {
Expand All @@ -17,13 +18,15 @@ interface InfiniteScrollData<T> {
hasNextPage: boolean;
isSuccess: boolean;
isError: boolean;
errorStatus: string | null;
}

export default function useInfiniteFetch<T, R>({
fetch,
initialPageParam = 0,
getNextPageParam,
startFetching = true,
showError = true,
}: UseInfiniteFetchProps<R>): InfiniteScrollData<T> {
const { showBoundary } = useErrorBoundary();

Expand All @@ -32,6 +35,8 @@ export default function useInfiniteFetch<T, R>({
const [isLoading, setIsLoading] = useState<boolean>(false);
const [isSuccess, setIsSuccess] = useState<boolean>(false);
const [isError, setIsError] = useState<boolean>(false);
const [errorStatus, setErrorStatus] = useState<string | null>(null);

const [hasNextPage, setHasNextPage] = useState<boolean>(true);
const [totalLength, setTotalLength] = useState<number>(0);

Expand All @@ -53,6 +58,7 @@ export default function useInfiniteFetch<T, R>({
setIsLoading(true);
setIsError(false);
setIsSuccess(false);
setErrorStatus(null);
try {
const lastPage = await fetch(currentPageParam);
const nextPageParam = getNextPageParam(currentPageParam, lastPage);
Expand All @@ -62,9 +68,14 @@ export default function useInfiniteFetch<T, R>({
setHasNextPage(nextPageParam !== undefined);
setIsSuccess(true);
} catch (error) {
showBoundary(error);
setIsError(true);
setIsSuccess(false);
if (error instanceof Error) {
setErrorStatus(error.message);
}
if (showError) {
showBoundary(error);
}
} finally {
setIsLoading(false);
}
Expand All @@ -91,5 +102,6 @@ export default function useInfiniteFetch<T, R>({
hasNextPage,
isSuccess,
isError,
errorStatus,
};
}
15 changes: 10 additions & 5 deletions admin/src/pages/LotteryParticipantList/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useEffect, useMemo, useRef, useState } from "react";
import { FormEvent, useEffect, useMemo, useRef, useState } from "react";
import { useCookies } from "react-cookie";
import { useNavigate } from "react-router-dom";
import { LotteryAPI } from "@/apis/lotteryAPI";
Expand Down Expand Up @@ -123,6 +123,11 @@ export default function LotteryParticipantList() {
patchLotteryExpectation(id);
};

const handleSubmitSearch = (e: FormEvent) => {
e.preventDefault();
handleRefetch();
};

const expectations = useMemo(
() =>
expectation.map((participant) => [
Expand Down Expand Up @@ -176,15 +181,15 @@ export default function LotteryParticipantList() {
</p>
</div>

<div className="flex gap-2">
<form className="flex gap-2" onSubmit={handleSubmitSearch}>
<input
ref={phoneNumberInputRef}
className="border border-neutral-950 rounded-lg text-neutral-950 h-body-1-medium"
/>
<Button buttonSize="sm" onClick={handleRefetch}>
<Button buttonSize="sm" type="submit">
검색
</Button>
</div>
</form>
</div>

<Table
Expand All @@ -194,7 +199,7 @@ export default function LotteryParticipantList() {
dataLastItem={targetRef}
/>

<Button buttonSize="lg" onClick={handleLotteryWinner}>
<Button buttonSize="lg" type="button" onClick={handleLotteryWinner}>
당첨자 보러가기
</Button>
</div>
Expand Down
77 changes: 57 additions & 20 deletions admin/src/pages/LotteryWinner/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,16 @@ import { useEffect, useState } from "react";
import { useNavigate } from "react-router-dom";
import { LotteryAPI } from "@/apis/lotteryAPI";
import Button from "@/components/Button";
import Suspense from "@/components/Suspense";
import TabHeader from "@/components/TabHeader";
import { ERROR_MAP } from "@/constants/common";
import useFetch from "@/hooks/useFetch";
import { LotteryEventType } from "@/types/lottery";
import { GetLotteryResponse, PostLotteryWinnerResponse } from "@/types/lotteryApi";
import {
DeleteLotteryWinnerResponse,
GetLotteryResponse,
PostLotteryWinnerResponse,
} from "@/types/lotteryApi";

export default function LotteryWinner() {
const navigate = useNavigate();
Expand All @@ -18,8 +24,22 @@ export default function LotteryWinner() {
fetchData: getLotteryEvent,
} = useFetch<GetLotteryResponse>((_, token) => LotteryAPI.getLottery(token));

const { isSuccess: isSuccessPostLottery, fetchData: postLottery } =
useFetch<PostLotteryWinnerResponse>((_, token) => LotteryAPI.postLotteryWinner(token));
const {
isSuccess: isSuccessPostLottery,
isLoading: isLoadingPostLottery,
isError: isErrorPostLottery,
errorStatus: lotteryPostErrorStatus,
fetchData: postLottery,
} = useFetch<PostLotteryWinnerResponse>(
(_, token) => LotteryAPI.postLotteryWinner(token),
false
);

const { isSuccess: isSuccessDeleteLottery, fetchData: deleteLottery } =
useFetch<DeleteLotteryWinnerResponse>(
(_, token) => LotteryAPI.deleteLotteryWinner(token),
false
);

useEffect(() => {
getLotteryEvent();
Expand All @@ -34,31 +54,48 @@ export default function LotteryWinner() {
navigate("/lottery/winner-list");
}
}, [isSuccessPostLottery]);
useEffect(() => {
if (isErrorPostLottery && lotteryPostErrorStatus === ERROR_MAP.CONFLICT) {
const isDelete = confirm("이미 추첨한 이벤트입니다. 삭제 후 다시 추첨하시겠습니까?");
if (isDelete) {
deleteLottery();
}
}
}, [isErrorPostLottery, lotteryPostErrorStatus]);
useEffect(() => {
if (isSuccessDeleteLottery) {
postLottery();
}
}, [isSuccessDeleteLottery]);

const handleLottery = () => {
postLottery();
};

return (
<div className="flex flex-col items-center h-screen">
<TabHeader />
<Suspense isLoading={isLoadingPostLottery}>
<div className="flex flex-col items-center h-screen">
<TabHeader />

<div className="flex flex-col h-full items-center justify-center gap-8 pb-40">
<div className="flex border">
<p className="px-6 py-4 w-[200px] bg-gray-50 h-body-1-bold">전체 참여자 수</p>
<p className="px-6 py-4 w-[200px] h-body-1-regular">
{currentLottery.appliedCount}
</p>
<p className="px-6 py-4 w-[200px] bg-gray-50 h-body-1-bold">당첨자 수</p>
<p className="px-6 py-4 w-[200px] h-body-1-regular">
{currentLottery.winnerCount}
</p>
</div>
<div className="flex flex-col h-full items-center justify-center gap-8 pb-40">
<div className="flex border">
<p className="px-6 py-4 w-[200px] bg-gray-50 h-body-1-bold">
전체 참여자 수
</p>
<p className="px-6 py-4 w-[200px] h-body-1-regular">
{currentLottery.appliedCount}
</p>
<p className="px-6 py-4 w-[200px] bg-gray-50 h-body-1-bold">당첨자 수</p>
<p className="px-6 py-4 w-[200px] h-body-1-regular">
{currentLottery.winnerCount}
</p>
</div>

<Button buttonSize="lg" onClick={handleLottery}>
당첨자 추첨하기
</Button>
<Button buttonSize="lg" onClick={handleLottery}>
당첨자 추첨하기
</Button>
</div>
</div>
</div>
</Suspense>
);
}
Loading
Loading