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

[Feat] 어드민 UI 구현 #129

Merged
merged 12 commits into from
Aug 12, 2024
5 changes: 3 additions & 2 deletions admin/public/assets/icons/calendar.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
12 changes: 7 additions & 5 deletions admin/src/apis/lotteryAPI.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import {
GetLotteryResponse,
GetLotteryWinnerParams,
GetLotteryWinnerResponse,
PostLotteryWinnerParams,
PostLotteryWinnerResponse,
} from "@/types/lotteryApi";
import { fetchWithTimeout } from "@/utils/fetchWithTimeout";
Expand All @@ -21,8 +20,10 @@ export const LotteryAPI = {
resolve([
{
lotteryEventId: 1,
startDate: "2024-07-26 00:00",
endDate: "2024-08-25 23:59",
startDate: "2024-07-26",
startTime: "00:00",
endDate: "2024-08-25",
endTime: "23:59",
appliedCount: 1000000,
winnerCount: 363,
},
Expand All @@ -38,10 +39,10 @@ export const LotteryAPI = {
throw error;
}
},
async postLotteryWinner({ id }: PostLotteryWinnerParams): Promise<PostLotteryWinnerResponse> {
async postLotteryWinner(): Promise<PostLotteryWinnerResponse> {
try {
return new Promise((resolve) => resolve({ message: "요청에 성공하였습니다." }));
const response = await fetchWithTimeout(`${baseURL}/${id}/winner`, {
const response = await fetchWithTimeout(`${baseURL}/winner`, {
method: "POST",
headers: headers,
});
Expand Down Expand Up @@ -84,6 +85,7 @@ export const LotteryAPI = {
},
],
isLastPage: false,
totalParticipants: 10000,
})
);
const response = await fetchWithTimeout(
Expand Down
2 changes: 2 additions & 0 deletions admin/src/apis/rushAPI.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ export const RushAPI = {
},
],
isLastPage: false,
totalParticipants: 10000,
})
);
const response = await fetchWithTimeout(
Expand Down Expand Up @@ -95,6 +96,7 @@ export const RushAPI = {
},
],
isLastPage: false,
totalParticipants: 10000,
})
);
const response = await fetchWithTimeout(
Expand Down
5 changes: 1 addition & 4 deletions admin/src/components/Layout/index.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,12 @@
import { Outlet } from "react-router-dom";
import { RushEventContext } from "@/contexts/rushEventContext";
import Header from "../Header";

export default function Layout() {
return (
<div className="overflow-hidden h-screen">
<Header />
<div className="mb-[80px]">
<RushEventContext>
<Outlet />
</RushEventContext>
<Outlet />
</div>
</div>
);
Expand Down
5 changes: 3 additions & 2 deletions admin/src/components/Table/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,16 @@ import { ReactNode, RefObject, forwardRef } from "react";
interface TableProps {
headers: ReactNode[];
data: ReactNode[][];
height?: string | number;
dataLastItem?: RefObject<HTMLTableRowElement>;
}

const Table = forwardRef<HTMLDivElement, TableProps>(function Table(
{ headers, data, dataLastItem },
{ headers, data, height = 600, dataLastItem },
ref
) {
return (
<div ref={ref} className="relative sm:rounded-lg w-[1560px] h-[600px] border">
<div ref={ref} className="relative sm:rounded-lg w-[1560px] border" style={{ height }}>
<div className="overflow-y-auto h-full table-contents">
<table className="w-full text-sm rtl:text-right text-gray-500 text-center">
<thead className="sticky top-0 z-[5] text-gray-700 bg-gray-50">
Expand Down
13 changes: 13 additions & 0 deletions admin/src/components/TextField/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { ComponentProps } from "react";

interface TextFieldProps extends ComponentProps<"textarea"> {}

export default function TextField({ ...restProps }: TextFieldProps) {
return (
<textarea
className="resize-none focus:outline-none w-full text-black"
spellCheck={false}
{...restProps}
/>
);
}
9 changes: 9 additions & 0 deletions admin/src/constants/lottery.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export const LOTTERY_HEADER = [
"시작 날짜",
"시작 시간",
"종료 날짜",
"종료 시간",
"활성화 기간",
"추첨 당첨 인원 수",
"진행 상태",
];
20 changes: 13 additions & 7 deletions admin/src/constants/rush.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
export const RUSH_SECTION = {
EVENT_LIST: "event_list",
APPLICANT_LIST: "applicant_list",
SELECTION_MANAGE: "selection_manage",
GIFT_MANAGE: "gift_manage",
};
export type RushSectionType = (typeof RUSH_SECTION)[keyof typeof RUSH_SECTION];
export const EVENT_LIST_HEADER = [
"ID",
"이벤트 진행 날짜",
"오픈 시간",
"종료 시간",
"활성화 시간",
"선택지 관리",
"경품 관리",
"선착순 당첨 인원 수",
"진행 상태",
"참여자 리스트 보기",
"관리",
];
8 changes: 7 additions & 1 deletion admin/src/contexts/rushEventContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,16 @@ import {
RushEventAction,
RushEventDispatchType,
RushEventStateType,
RushPrizeType,
} from "@/types/rush";

export const RushEventStateContext = createContext<RushEventStateType | null>(null);
export const RushEventDispatchContext = createContext<RushEventDispatchType | null>(null);

const initialState: RushEventStateType = {
rushList: [],
selectOptions: [],
prize: {} as RushPrizeType,
};

const casperCustomReducer = (
Expand All @@ -20,7 +23,10 @@ const casperCustomReducer = (
switch (action.type) {
case RUSH_ACTION.SET_EVENT_LIST:
return { ...state, rushList: action.payload };

case RUSH_ACTION.SET_OPTION:
return { ...state, selectOptions: action.payload };
case RUSH_ACTION.SET_PRIZE:
return { ...state, prize: action.payload };
default:
return state;
}
Expand Down
65 changes: 35 additions & 30 deletions admin/src/features/Rush/EventList.tsx
Original file line number Diff line number Diff line change
@@ -1,33 +1,18 @@
import { useEffect } from "react";
import { useNavigate } from "react-router-dom";
import Button from "@/components/Button";
import DatePicker from "@/components/DatePicker";
import Table from "@/components/Table";
import TimePicker from "@/components/TimePicker";
import { RUSH_SECTION, RushSectionType } from "@/constants/rush";
import { EVENT_LIST_HEADER } from "@/constants/rush";
import useRushEventDispatchContext from "@/hooks/useRushEventDispatchContext";
import useRushEventStateContext from "@/hooks/useRushEventStateContext";
import { RUSH_ACTION } from "@/types/rush";
import { getTimeDifference } from "@/utils/getTimeDifference";

interface EventListProps {
handleSelectSection: (idx: number, section: RushSectionType) => void;
}

const EVENT_LIST_HEADER = [
"ID",
"이벤트 진행 날짜",
"오픈 시간",
"종료 시간",
"활성화 시간",
"선택지 관리",
"경품 관리",
"선착순 당첨 인원 수",
"진행 상태",
"참여자 리스트 보기",
"관리",
];
export default function EventList() {
const navigate = useNavigate();

export default function EventList({ handleSelectSection }: EventListProps) {
const { rushList } = useRushEventStateContext();
const dispatch = useRushEventDispatchContext();

Expand Down Expand Up @@ -67,10 +52,10 @@ export default function EventList({ handleSelectSection }: EventListProps) {
});
}, []);

const handleChangeItem = (key: string, changeIdx: number, date: string) => {
const handleChangeItem = (key: string, changeIdx: number, text: string | number) => {
const updatedTableItemList = rushList.map((item, idx) => {
if (idx === changeIdx) {
return { ...item, [key]: date };
return { ...item, [key]: text };
}
return { ...item };
});
Expand All @@ -84,27 +69,47 @@ export default function EventList({ handleSelectSection }: EventListProps) {
item.rushEventId,
<DatePicker
date={item.eventDate}
onChangeDate={(date) => handleChangeItem("event_date", idx, date)}
onChangeDate={(date) => handleChangeItem("eventDate", idx, date)}
/>,
<TimePicker
time={item.openTime}
onChangeTime={(time) => handleChangeItem("open_time", idx, time)}
onChangeTime={(time) => handleChangeItem("openTime", idx, time)}
/>,
<TimePicker
time={item.closeTime}
onChangeTime={(time) => handleChangeItem("close_time", idx, time)}
onChangeTime={(time) => handleChangeItem("closeTime", idx, time)}
/>,
getTimeDifference(item.openTime, item.closeTime),
<Button buttonSize="sm">선택지 관리</Button>,
<Button buttonSize="sm">경품 관리</Button>,
<div className="flex justify-between">
<p>{item.winnerCount}</p>
<p>편집</p>
<Button
buttonSize="sm"
onClick={() =>
navigate("/rush/select-form", { state: { id: item.rushEventId } })
}
>
선택지 관리
</Button>,
<Button
buttonSize="sm"
onClick={() =>
navigate("/rush/prize-form", { state: { id: item.rushEventId } })
}
>
경품 관리
</Button>,
<div className="flex w-full border-b">
<input
value={item.winnerCount}
onChange={(e) =>
handleChangeItem("winnerCount", idx, parseInt(e.target.value) || 0)
}
/>
</div>,
"오픈 전",
<Button
buttonSize="sm"
onClick={() => handleSelectSection(idx, RUSH_SECTION.APPLICANT_LIST)}
onClick={() =>
navigate("/rush/winner-list", { state: { id: item.rushEventId } })
}
>
참여자 리스트 보기
</Button>,
Expand Down
10 changes: 10 additions & 0 deletions admin/src/features/Rush/Layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { Outlet } from "react-router-dom";
import { RushEventContext } from "@/contexts/rushEventContext";

export default function RushLayout() {
return (
<RushEventContext>
<Outlet />
</RushEventContext>
);
}
4 changes: 4 additions & 0 deletions admin/src/hooks/useInfiniteFetch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ interface UseInfiniteFetchProps<R> {

interface InfiniteScrollData<T> {
data: T[];
totalLength: number;
fetchNextPage: () => void;
refetch: () => void;
hasNextPage: boolean;
Expand All @@ -29,6 +30,7 @@ export default function useInfiniteFetch<T>({
const [isSuccess, setIsSuccess] = useState<boolean>(false);
const [isError, setIsError] = useState<boolean>(false);
const [hasNextPage, setHasNextPage] = useState<boolean>(true);
const [totalLength, setTotalLength] = useState<number>(0);

const fetchNextPage = useCallback(async () => {
if (!hasNextPage || isLoading || currentPageParam === undefined) return;
Expand All @@ -41,6 +43,7 @@ export default function useInfiniteFetch<T>({
setData([...data, ...lastPage.data]);
setCurrentPageParam(nextPageParam);
setHasNextPage(nextPageParam !== undefined);
setTotalLength(lastPage.totalParticipants);
setIsSuccess(true);
} catch (error) {
setIsError(true);
Expand All @@ -63,6 +66,7 @@ export default function useInfiniteFetch<T>({

return {
data,
totalLength,
fetchNextPage,
refetch,
hasNextPage,
Expand Down
Loading
Loading