setIsVisibleOptions(false)}
+ />
+
+
setIsVisibleOptions(!isVisibleOptions)}>
+ {options[selectedIdx]}
+
+ {isVisibleOptions && (
+
+
+ {options.map((option, idx) => (
+
handleClick(idx)}
+ className="break-keep text-nowrap"
+ >
+ {option}
+
+ ))}
+
+
+ )}
+
+ >
+ );
+}
diff --git a/admin/src/components/Header/index.tsx b/admin/src/components/Header/index.tsx
new file mode 100644
index 00000000..5e0977c3
--- /dev/null
+++ b/admin/src/components/Header/index.tsx
@@ -0,0 +1,7 @@
+export default function Header() {
+ return (
+
+ 현대자동차 캐스퍼 일렉트릭 신차 출시 이벤트 어드민
+
+ );
+}
diff --git a/admin/src/components/Input/index.tsx b/admin/src/components/Input/index.tsx
new file mode 100644
index 00000000..91e18d73
--- /dev/null
+++ b/admin/src/components/Input/index.tsx
@@ -0,0 +1,19 @@
+import { HTMLProps } from "react";
+
+interface InputProps extends HTMLProps
{
+ label?: string;
+}
+
+export default function Input({ label, value, onChange, ...restProps }: InputProps) {
+ return (
+
+ );
+}
diff --git a/admin/src/components/Modal/index.tsx b/admin/src/components/Modal/index.tsx
new file mode 100644
index 00000000..48f849ee
--- /dev/null
+++ b/admin/src/components/Modal/index.tsx
@@ -0,0 +1,27 @@
+import { PropsWithChildren } from "react";
+
+export interface ModalProps extends PropsWithChildren {
+ handleClose: () => void;
+}
+
+export default function Modal({ handleClose, children }: ModalProps) {
+ return (
+
+
+
+ {children}
+
+
+
+
+ );
+}
diff --git a/admin/src/components/TabHeader/index.tsx b/admin/src/components/TabHeader/index.tsx
new file mode 100644
index 00000000..a38c422c
--- /dev/null
+++ b/admin/src/components/TabHeader/index.tsx
@@ -0,0 +1,32 @@
+import { cva } from "class-variance-authority";
+
+interface TabHeaderProps {
+ tabList: string[];
+ handleClickTab: (idx: number) => void;
+ selectedIdx: number;
+}
+
+const TabButtonVariants = cva(`border-b-2`, {
+ variants: {
+ selected: {
+ true: "h-body-1-bold border-neutral-950",
+ false: "h-body-1-regular border-transparent",
+ },
+ },
+});
+
+export default function TabHeader({ tabList, selectedIdx, handleClickTab }: TabHeaderProps) {
+ return (
+
+ {tabList.map((tab, idx) => (
+
+ ))}
+
+ );
+}
diff --git a/admin/src/components/Table/index.tsx b/admin/src/components/Table/index.tsx
new file mode 100644
index 00000000..574e886f
--- /dev/null
+++ b/admin/src/components/Table/index.tsx
@@ -0,0 +1,43 @@
+import { ReactNode } from "react";
+
+interface TableProps {
+ headers: ReactNode[];
+ data: ReactNode[][];
+}
+
+export default function Table({ headers, data }: TableProps) {
+ return (
+
+
+
+
+
+ {headers.map((header, idx) => (
+
+ {header}
+ |
+ ))}
+
+
+
+ {data.map((tableData, idx) => (
+
+ {tableData.map((dataNode, idx) => (
+
+ {dataNode}
+ |
+ ))}
+
+ ))}
+
+
+
+
+ );
+}
diff --git a/admin/src/hooks/useModal.tsx b/admin/src/hooks/useModal.tsx
new file mode 100644
index 00000000..fbd9714f
--- /dev/null
+++ b/admin/src/hooks/useModal.tsx
@@ -0,0 +1,28 @@
+import { useEffect, useState } from "react";
+import Modal, { ModalProps } from "@/components/Modal";
+
+export default function useModal() {
+ const [isOpen, setIsOpen] = useState(false);
+
+ useEffect(() => {
+ return () => {
+ document.body.style.overflow = "unset";
+ };
+ }, []);
+
+ const handleOpenModal = () => {
+ document.body.style.overflow = "hidden";
+ setIsOpen(true);
+ };
+
+ const handleCloseModal = () => {
+ document.body.style.overflow = "unset";
+ setIsOpen(false);
+ };
+
+ const ModalComponent = ({ children }: Omit) => {
+ return isOpen ? {children} : null;
+ };
+
+ return { handleOpenModal, ModalComponent };
+}
diff --git a/admin/src/index.css b/admin/src/index.css
index 42c578dd..8d716516 100644
--- a/admin/src/index.css
+++ b/admin/src/index.css
@@ -2,44 +2,41 @@
@tailwind components;
@tailwind utilities;
+input:focus {
+ outline: none;
+}
+
@font-face {
- font-family: "HyundaiSansHeadOffice-Bold";
- src: url("/public/fonts/hyundai-sans/HyundaiSansHeadKROTFBold.otf")
- format("opentype");
+ font-family: "HyundaiSansHeadOffice-Bold";
+ src: url("/public/fonts/hyundai-sans/HyundaiSansHeadKROTFBold.otf") format("opentype");
}
@font-face {
- font-family: "HyundaiSansHeadOffice-Medium";
- src: url("/public/fonts/hyundai-sans/HyundaiSansHeadKROTFMedium.otf")
- format("opentype");
+ font-family: "HyundaiSansHeadOffice-Medium";
+ src: url("/public/fonts/hyundai-sans/HyundaiSansHeadKROTFMedium.otf") format("opentype");
}
@font-face {
- font-family: "HyundaiSansHeadOffice-Regular";
- src: url("/public/fonts/hyundai-sans/HyundaiSansHeadKROTFRegular.otf")
- format("opentype");
+ font-family: "HyundaiSansHeadOffice-Regular";
+ src: url("/public/fonts/hyundai-sans/HyundaiSansHeadKROTFRegular.otf") format("opentype");
}
@font-face {
- font-family: "HyundaiSansHeadOffice-Light";
- src: url("/public/fonts/hyundai-sans/HyundaiSansHeadKROTFLight.otf")
- format("opentype");
+ font-family: "HyundaiSansHeadOffice-Light";
+ src: url("/public/fonts/hyundai-sans/HyundaiSansHeadKROTFLight.otf") format("opentype");
}
@font-face {
- font-family: "HyundaiSansTextOffice-Bold";
- src: url("/public/fonts/hyundai-sans/HyundaiSansTextKROTFBold.otf")
- format("opentype");
+ font-family: "HyundaiSansTextOffice-Bold";
+ src: url("/public/fonts/hyundai-sans/HyundaiSansTextKROTFBold.otf") format("opentype");
}
@font-face {
- font-family: "HyundaiSansTextOffice-Medium";
- src: url("/public/fonts/hyundai-sans/HyundaiSansTextKROTFMedium.otf")
- format("opentype");
+ font-family: "HyundaiSansTextOffice-Medium";
+ src: url("/public/fonts/hyundai-sans/HyundaiSansTextKROTFMedium.otf") format("opentype");
}
@font-face {
- font-family: "HyundaiSansTextOffice-Regular";
- src: url("/public/fonts/hyundai-sans/HyundaiSansTextKROTFRegular.otf")
- format("opentype");
+ font-family: "HyundaiSansTextOffice-Regular";
+ src: url("/public/fonts/hyundai-sans/HyundaiSansTextKROTFRegular.otf") format("opentype");
}
diff --git a/admin/src/pages/Login/index.tsx b/admin/src/pages/Login/index.tsx
new file mode 100644
index 00000000..19fa3d5e
--- /dev/null
+++ b/admin/src/pages/Login/index.tsx
@@ -0,0 +1,87 @@
+import { ChangeEvent, useState } from "react";
+import Button from "@/components/Button";
+import Dropdown from "@/components/Dropdown";
+import Header from "@/components/Header";
+import Input from "@/components/Input";
+import TabHeader from "@/components/TabHeader";
+import Table from "@/components/Table";
+import useModal from "@/hooks/useModal";
+
+export default function Login() {
+ const [selectedIdx, setSelectedIdx] = useState(0);
+ const [value, setValue] = useState("");
+
+ const { handleOpenModal, ModalComponent } = useModal();
+
+ const [selectedDropdownIdx, setSelectedDropdownIdx] = useState(0);
+
+ const headers = [
+ "ID",
+ "이벤트 진행 날짜",
+ "오픈 시간",
+ "종료 시간",
+ "활성화 시간",
+ "선택지 관리",
+ "경품 관리",
+ "선착순 당첨 인원 수",
+ "진행 상태",
+ setSelectedDropdownIdx(idx)}
+ />,
+ "관리",
+ ];
+ const data = new Array(20).fill(null).map(() => [
+ 7,
+ "2024-07-19",
+ "22:00:00",
+ "22:10:00",
+ "00시간 10분 00초",
+ ,
+ ,
+ ,
+ "오픈 전",
+ ,
+ ,
+ ]);
+
+ return (
+ <>
+
+ setSelectedIdx(idx)}
+ />
+
+
+
+
+ setValue((e as ChangeEvent).target.value)}
+ />
+ setValue((e as ChangeEvent).target.value)}
+ />
+
+
+ hihi
+
+
+
+ >
+ );
+}
diff --git a/admin/src/router.tsx b/admin/src/router.tsx
index 5455aab1..d500b1c3 100644
--- a/admin/src/router.tsx
+++ b/admin/src/router.tsx
@@ -1,8 +1,9 @@
import { createBrowserRouter } from "react-router-dom";
+import Login from "./pages/Login";
export const router = createBrowserRouter([
{
path: "/",
- element: herd
,
+ element: ,
},
]);