Skip to content

Commit

Permalink
Merge pull request #185 from softeerbootcamp4th/fix/#184-admin-lottery
Browse files Browse the repository at this point in the history
[Fix] 어드민 오류 수정
  • Loading branch information
sooyeoniya authored Aug 22, 2024
2 parents cf55683 + 27f5f78 commit 3dba62d
Show file tree
Hide file tree
Showing 13 changed files with 175 additions and 42 deletions.
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

0 comments on commit 3dba62d

Please sign in to comment.