-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
[Feat] 어드민 공통 컴포넌트 구현
- Loading branch information
Showing
12 changed files
with
339 additions
and
22 deletions.
There are no files selected for viewing
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
import { HTMLProps } from "react"; | ||
import { cva } from "class-variance-authority"; | ||
|
||
interface ButtonProps extends HTMLProps<HTMLButtonElement> { | ||
isValid?: boolean; | ||
type: "lg" | "sm"; | ||
} | ||
|
||
const ButtonVariants = cva(`transition-all`, { | ||
variants: { | ||
isValid: { | ||
true: "text-neutral-950 border-neutral-950 bg-white hover:bg-neutral-100", | ||
false: "text-neutral-300 border-neutral-300 bg-neutral-100", | ||
}, | ||
type: { | ||
lg: "w-[266px] rounded-full py-[16px] border-2", | ||
sm: "inline px-[12px] py-[8px] rounded-xl border", | ||
}, | ||
}, | ||
}); | ||
|
||
export default function Button({ isValid = true, type, children, ...restProps }: ButtonProps) { | ||
return ( | ||
<button className={ButtonVariants({ isValid, type })} disabled={!isValid} {...restProps}> | ||
{children} | ||
</button> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
import { useState } from "react"; | ||
|
||
interface DropdownProps { | ||
options: string[]; | ||
selectedIdx: number; | ||
handleClickOption: (idx: number) => void; | ||
} | ||
|
||
export default function Dropdown({ options, selectedIdx, handleClickOption }: DropdownProps) { | ||
const [isVisibleOptions, setIsVisibleOptions] = useState<boolean>(false); | ||
|
||
const handleClick = (idx: number) => { | ||
handleClickOption(idx); | ||
setIsVisibleOptions(false); | ||
}; | ||
|
||
return ( | ||
<> | ||
<div | ||
className="fixed w-screen h-screen left-0 top-0" | ||
onClick={() => setIsVisibleOptions(false)} | ||
/> | ||
<div className="relative inline-block text-left"> | ||
<div onClick={() => setIsVisibleOptions(!isVisibleOptions)}> | ||
{options[selectedIdx]} | ||
</div> | ||
{isVisibleOptions && ( | ||
<div className="origin-top-right absolute right-0 mt-2 rounded-md shadow-lg bg-white ring-1 ring-black ring-opacity-5"> | ||
<div className="p-[16px] flex flex-col gap-2"> | ||
{options.map((option, idx) => ( | ||
<p | ||
key={`dropdown-${option}-${idx}`} | ||
onClick={() => handleClick(idx)} | ||
className="break-keep text-nowrap" | ||
> | ||
{option} | ||
</p> | ||
))} | ||
</div> | ||
</div> | ||
)} | ||
</div> | ||
</> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
export default function Header() { | ||
return ( | ||
<header className="w-full h-[80px] bg-neutral-700 text-neutral-200 h-body-1-bold px-10 flex items-center"> | ||
현대자동차 캐스퍼 일렉트릭 신차 출시 이벤트 어드민 | ||
</header> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
import { HTMLProps } from "react"; | ||
|
||
interface InputProps extends HTMLProps<HTMLInputElement> { | ||
label?: string; | ||
} | ||
|
||
export default function Input({ label, value, onChange, ...restProps }: InputProps) { | ||
return ( | ||
<div className="flex items-center"> | ||
{label && <p className="text-neutral-950 h-body-1-bold mx-[20px]">{label}</p>} | ||
<input | ||
className="p-[16px] border border-neutral-950 rounded-lg text-neutral-950 w-[360px] h-body-1-medium" | ||
value={value} | ||
onChange={onChange} | ||
{...restProps} | ||
/> | ||
</div> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
import { PropsWithChildren } from "react"; | ||
|
||
export interface ModalProps extends PropsWithChildren { | ||
handleClose: () => void; | ||
} | ||
|
||
export default function Modal({ handleClose, children }: ModalProps) { | ||
return ( | ||
<div className="fixed w-full h-full left-0 top-0 z-20"> | ||
<div | ||
className="absolute left-0 top-0 w-[100%] h-[100%] bg-black/[.4]" | ||
onClick={handleClose} | ||
/> | ||
<div className="absolute left-[50%] top-[50%] translate-y-[-50%] translate-x-[-50%] bg-white p-[80px] rounded-3xl"> | ||
{children} | ||
|
||
<button onClick={handleClose}> | ||
<img | ||
className="absolute right-[32px] top-[32px]" | ||
alt="모달 닫기 버튼" | ||
src="/assets/icons/close.svg" | ||
/> | ||
</button> | ||
</div> | ||
</div> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 ( | ||
<div className="w-full h-[80px] flex px-[40px] gap-[40px]"> | ||
{tabList.map((tab, idx) => ( | ||
<button | ||
key={idx} | ||
className={TabButtonVariants({ selected: selectedIdx === idx })} | ||
onClick={() => handleClickTab(idx)} | ||
> | ||
{tab} | ||
</button> | ||
))} | ||
</div> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
import { ReactNode } from "react"; | ||
|
||
interface TableProps { | ||
headers: ReactNode[]; | ||
data: ReactNode[][]; | ||
} | ||
|
||
export default function Table({ headers, data }: TableProps) { | ||
return ( | ||
<div className="relative sm:rounded-lg w-[1560px] h-[600px] border"> | ||
<div className="overflow-y-auto h-full"> | ||
<table className="w-full text-sm rtl:text-right text-gray-500 dark:text-gray-400 text-center"> | ||
<thead className="sticky top-0 z-10 text-gray-700 bg-gray-50 dark:bg-gray-700 dark:text-gray-400"> | ||
<tr> | ||
{headers.map((header, idx) => ( | ||
<th key={idx} scope="col" className="px-6 py-3 h-body-2-medium"> | ||
{header} | ||
</th> | ||
))} | ||
</tr> | ||
</thead> | ||
<tbody> | ||
{data.map((tableData, idx) => ( | ||
<tr | ||
key={`table-data-${idx}`} | ||
className="bg-white border-b dark:bg-gray-800 dark:border-gray-700" | ||
> | ||
{tableData.map((dataNode, idx) => ( | ||
<td | ||
key={`${headers[idx]}-data-${idx}`} | ||
className="px-6 py-4 h-body-2-regular" | ||
> | ||
{dataNode} | ||
</td> | ||
))} | ||
</tr> | ||
))} | ||
</tbody> | ||
</table> | ||
</div> | ||
</div> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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<ModalProps, "handleClose">) => { | ||
return isOpen ? <Modal handleClose={handleCloseModal}>{children}</Modal> : null; | ||
}; | ||
|
||
return { handleOpenModal, ModalComponent }; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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<number>(0); | ||
const [value, setValue] = useState<string>(""); | ||
|
||
const { handleOpenModal, ModalComponent } = useModal(); | ||
|
||
const [selectedDropdownIdx, setSelectedDropdownIdx] = useState(0); | ||
|
||
const headers = [ | ||
"ID", | ||
"이벤트 진행 날짜", | ||
"오픈 시간", | ||
"종료 시간", | ||
"활성화 시간", | ||
"선택지 관리", | ||
"경품 관리", | ||
"선착순 당첨 인원 수", | ||
"진행 상태", | ||
<Dropdown | ||
options={["옵션 1 : 총 선택 인원 13,245", "옵션 2 : 총 선택 인원 10,182"]} | ||
selectedIdx={selectedDropdownIdx} | ||
handleClickOption={(idx: number) => setSelectedDropdownIdx(idx)} | ||
/>, | ||
"관리", | ||
]; | ||
const data = new Array(20).fill(null).map(() => [ | ||
7, | ||
"2024-07-19", | ||
"22:00:00", | ||
"22:10:00", | ||
"00시간 10분 00초", | ||
<Button type="sm">선택지 관리</Button>, | ||
<Button type="sm">경품 관리</Button>, | ||
<div className="flex justify-between"> | ||
<p>315</p> | ||
<p>편집</p> | ||
</div>, | ||
"오픈 전", | ||
<Button type="sm">참여자 리스트 보기</Button>, | ||
<Button type="sm">삭제</Button>, | ||
]); | ||
|
||
return ( | ||
<> | ||
<Header /> | ||
<TabHeader | ||
tabList={["캐스퍼 일렉트릭 봇 만들기 추첨 이벤트", "선착순 밸런스 게임 이벤트"]} | ||
selectedIdx={selectedIdx} | ||
handleClickTab={(idx) => setSelectedIdx(idx)} | ||
/> | ||
<Button type="lg" onClick={handleOpenModal}> | ||
임시 저장 | ||
</Button> | ||
<Button type="sm">임시 저장</Button> | ||
<Button type="lg" isValid={false}> | ||
임시 저장 | ||
</Button> | ||
<Button type="sm" isValid={false}> | ||
임시 저장 | ||
</Button> | ||
<Input | ||
value={value} | ||
onChange={(e) => setValue((e as ChangeEvent<HTMLInputElement>).target.value)} | ||
/> | ||
<Input | ||
label="ID" | ||
value={value} | ||
onChange={(e) => setValue((e as ChangeEvent<HTMLInputElement>).target.value)} | ||
/> | ||
|
||
<ModalComponent> | ||
<div className="w-[200px] h-[128px]">hihi</div> | ||
</ModalComponent> | ||
|
||
<Table headers={headers} data={data} /> | ||
</> | ||
); | ||
} |
Oops, something went wrong.