Skip to content

Commit

Permalink
Merge pull request #129 from softeerbootcamp4th/feat/#125-admin-ui
Browse files Browse the repository at this point in the history
[Feat] 어드민 UI 구현
  • Loading branch information
jhj2713 authored Aug 12, 2024
2 parents 198c1d3 + 86b3cfb commit ef215ea
Show file tree
Hide file tree
Showing 24 changed files with 484 additions and 77 deletions.
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

0 comments on commit ef215ea

Please sign in to comment.