Compare commits
4 Commits
8403684df3
...
0af1028b7b
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0af1028b7b | ||
|
|
efdcde8b12 | ||
|
|
a7cad39b3c | ||
|
|
16d6cb5b30 |
@@ -4,11 +4,146 @@ import { useEffect, useState } from 'react';
|
|||||||
import { useRouter } from 'next/navigation';
|
import { useRouter } from 'next/navigation';
|
||||||
import { isAdminLoggedIn } from '../../lib/auth';
|
import { isAdminLoggedIn } from '../../lib/auth';
|
||||||
import LoginPage from '../login/page';
|
import LoginPage from '../login/page';
|
||||||
|
import Logout from '../../public/svg/logout';
|
||||||
|
|
||||||
|
const imgArrowDisabled = "http://localhost:3845/assets/6edcb2defc36a2bf4a05a3abe53b8da3d42b2cb4.svg";
|
||||||
|
const imgArrowDefault = "http://localhost:3845/assets/ad0cb4418492f1b020bb38a2ff038a331294ce87.svg";
|
||||||
|
const imgArrowNext = "http://localhost:3845/assets/6328cf96ee1169c1425c2ce55e7a2dcca0374508.svg";
|
||||||
|
|
||||||
|
interface User {
|
||||||
|
id: number;
|
||||||
|
joinDate: string;
|
||||||
|
name: string;
|
||||||
|
email: string;
|
||||||
|
role: string;
|
||||||
|
accountStatus: string;
|
||||||
|
accountManagement: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
type PaginationMove = "Previous" | "Next";
|
||||||
|
type PaginationStatus = "Default" | "Disabled";
|
||||||
|
|
||||||
|
function PaginationBtnMove({ status = "Default", move = "Previous" }: { status?: PaginationStatus; move?: PaginationMove }) {
|
||||||
|
const isDisabled = status === "Disabled";
|
||||||
|
const isNext = move === "Next";
|
||||||
|
|
||||||
|
if (isDisabled && isNext) {
|
||||||
|
return (
|
||||||
|
<div className="border border-[#eeeeee] border-solid relative rounded-[8px] shrink-0 size-[30px]">
|
||||||
|
<div className="overflow-clip relative rounded-[inherit] size-[30px]">
|
||||||
|
<div className="absolute flex items-center justify-center left-[5px] size-[20px] top-[5px]">
|
||||||
|
<div className="flex-none rotate-[180deg]">
|
||||||
|
<div className="overflow-clip relative size-[20px]">
|
||||||
|
<div className="absolute flex inset-[15.63%_30.21%] items-center justify-center">
|
||||||
|
<div className="flex-none h-[16.5px] rotate-[180deg] w-[9.5px]">
|
||||||
|
<div className="relative size-full">
|
||||||
|
<div className="absolute inset-0">
|
||||||
|
<img alt="" className="block max-w-none size-full" style={{ filter: 'opacity(0.88) brightness(0) saturate(100%) invert(88%)', opacity: 1 }} src={imgArrowNext} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isDisabled && !isNext) {
|
||||||
|
return (
|
||||||
|
<div className="border border-[#eeeeee] border-solid relative rounded-[8px] shrink-0 size-[30px]">
|
||||||
|
<div className="overflow-clip relative rounded-[inherit] size-[30px]">
|
||||||
|
<div className="absolute flex items-center justify-center left-[5px] size-[20px] top-[5px]">
|
||||||
|
<div className="flex-none rotate-[180deg]">
|
||||||
|
<div className="overflow-clip relative size-[20px]">
|
||||||
|
<div className="absolute inset-[15.63%_30.21%]">
|
||||||
|
<div className="absolute inset-0">
|
||||||
|
<img alt="" className="block max-w-none size-full" style={{ filter: 'opacity(0.88) brightness(0) saturate(100%) invert(88%)', opacity: 1 }} src={imgArrowDefault} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isNext) {
|
||||||
|
return (
|
||||||
|
<div className="border border-[#eeeeee] border-solid relative rounded-[8px] shrink-0 size-[30px] cursor-pointer hover:bg-gray-50 transition-colors">
|
||||||
|
<div className="overflow-clip relative rounded-[inherit] size-[30px]">
|
||||||
|
<div className="absolute flex items-center justify-center left-[5px] size-[20px] top-[5px]">
|
||||||
|
<div className="flex-none rotate-[180deg]">
|
||||||
|
<div className="overflow-clip relative size-[20px]">
|
||||||
|
<div className="absolute flex inset-[15.63%_30.21%] items-center justify-center">
|
||||||
|
<div className="flex-none h-[16.5px] rotate-[180deg] w-[9.5px]">
|
||||||
|
<div className="relative size-full">
|
||||||
|
<div className="absolute inset-0">
|
||||||
|
<img alt="" className="block max-w-none size-full" src={imgArrowNext} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="border border-[#eeeeee] border-solid relative rounded-[8px] shrink-0 size-[30px] cursor-pointer hover:bg-gray-50 transition-colors">
|
||||||
|
<div className="overflow-clip relative rounded-[inherit] size-[30px]">
|
||||||
|
<div className="absolute flex items-center justify-center left-[5px] size-[20px] top-[5px]">
|
||||||
|
<div className="flex-none rotate-[180deg]">
|
||||||
|
<div className="overflow-clip relative size-[20px]">
|
||||||
|
<div className="absolute inset-[15.63%_30.21%]">
|
||||||
|
<div className="absolute inset-0">
|
||||||
|
<img alt="" className="block max-w-none size-full" src={imgArrowDefault} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
export default function AdminHomePage() {
|
export default function AdminHomePage() {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const [isLoading, setIsLoading] = useState(true);
|
const [isLoading, setIsLoading] = useState(true);
|
||||||
const [isAuthenticated, setIsAuthenticated] = useState(false);
|
const [isAuthenticated, setIsAuthenticated] = useState(false);
|
||||||
|
const [selectedTab, setSelectedTab] = useState<'전체' | '학습자' | '강사' | '운영자'>('전체');
|
||||||
|
const [currentPage, setCurrentPage] = useState(1);
|
||||||
|
|
||||||
|
// 샘플 사용자 데이터
|
||||||
|
const [users, setUsers] = useState<User[]>([
|
||||||
|
{ id: 39, joinDate: '2026-01-15', name: '홍길동', email: 'hong@example.com', role: '학습자', accountStatus: '활성', accountManagement: '관리' },
|
||||||
|
{ id: 38, joinDate: '2026-01-14', name: '김철수', email: 'kim@example.com', role: '강사', accountStatus: '활성', accountManagement: '관리' },
|
||||||
|
{ id: 37, joinDate: '2026-01-13', name: '이영희', email: 'lee@example.com', role: '학습자', accountStatus: '활성', accountManagement: '관리' },
|
||||||
|
{ id: 36, joinDate: '2026-01-12', name: '박민수', email: 'park@example.com', role: '운영자', accountStatus: '활성', accountManagement: '관리' },
|
||||||
|
{ id: 35, joinDate: '2026-01-11', name: '최지영', email: 'choi@example.com', role: '학습자', accountStatus: '활성', accountManagement: '관리' },
|
||||||
|
{ id: 34, joinDate: '2026-01-10', name: '정대현', email: 'jung@example.com', role: '강사', accountStatus: '활성', accountManagement: '관리' },
|
||||||
|
{ id: 33, joinDate: '2026-01-09', name: '강미영', email: 'kang@example.com', role: '학습자', accountStatus: '활성', accountManagement: '관리' },
|
||||||
|
{ id: 32, joinDate: '2026-01-08', name: '윤성호', email: 'yoon@example.com', role: '학습자', accountStatus: '활성', accountManagement: '관리' },
|
||||||
|
{ id: 31, joinDate: '2026-01-07', name: '임수진', email: 'lim@example.com', role: '강사', accountStatus: '활성', accountManagement: '관리' },
|
||||||
|
{ id: 30, joinDate: '2026-01-06', name: '한지훈', email: 'han@example.com', role: '학습자', accountStatus: '활성', accountManagement: '관리' },
|
||||||
|
{ id: 29, joinDate: '2026-01-05', name: '송민경', email: 'song@example.com', role: '학습자', accountStatus: '활성', accountManagement: '관리' },
|
||||||
|
{ id: 28, joinDate: '2026-01-04', name: '오준혁', email: 'oh@example.com', role: '운영자', accountStatus: '활성', accountManagement: '관리' },
|
||||||
|
{ id: 27, joinDate: '2026-01-03', name: '류현우', email: 'ryu@example.com', role: '학습자', accountStatus: '활성', accountManagement: '관리' },
|
||||||
|
{ id: 26, joinDate: '2026-01-02', name: '신동욱', email: 'shin@example.com', role: '강사', accountStatus: '활성', accountManagement: '관리' },
|
||||||
|
{ id: 25, joinDate: '2026-01-01', name: '조은서', email: 'cho@example.com', role: '학습자', accountStatus: '활성', accountManagement: '관리' },
|
||||||
|
{ id: 24, joinDate: '2025-12-31', name: '배성민', email: 'bae@example.com', role: '학습자', accountStatus: '활성', accountManagement: '관리' },
|
||||||
|
{ id: 23, joinDate: '2025-12-30', name: '전혜진', email: 'jeon@example.com', role: '강사', accountStatus: '활성', accountManagement: '관리' },
|
||||||
|
{ id: 22, joinDate: '2025-12-29', name: '남궁준', email: 'namgung@example.com', role: '학습자', accountStatus: '활성', accountManagement: '관리' },
|
||||||
|
{ id: 21, joinDate: '2025-12-28', name: '서아름', email: 'seo@example.com', role: '학습자', accountStatus: '활성', accountManagement: '관리' },
|
||||||
|
{ id: 20, joinDate: '2025-12-27', name: '권태영', email: 'kwon@example.com', role: '운영자', accountStatus: '활성', accountManagement: '관리' },
|
||||||
|
]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// 관리자 인증 확인
|
// 관리자 인증 확인
|
||||||
@@ -36,13 +171,291 @@ export default function AdminHomePage() {
|
|||||||
return <LoginPage />;
|
return <LoginPage />;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const itemsPerPage = 13;
|
||||||
|
const totalPages = Math.ceil(users.length / itemsPerPage);
|
||||||
|
const pageNumbers = Array.from({ length: totalPages }, (_, i) => i + 1);
|
||||||
|
const startIndex = (currentPage - 1) * itemsPerPage;
|
||||||
|
const endIndex = startIndex + itemsPerPage;
|
||||||
|
const currentUsers = users.slice(startIndex, endIndex);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="bg-white relative size-full min-h-screen">
|
<div className="bg-white relative size-full min-h-screen">
|
||||||
<div className="flex items-center justify-center min-h-screen">
|
{/* 사이드바 */}
|
||||||
<div className="text-center">
|
<div className="absolute bg-white border-r border-[#eeeeee] border-solid box-border content-stretch flex flex-col gap-[45px] items-center left-0 min-h-[1080px] pb-8 pt-[30px] px-0 top-0 w-[250px]">
|
||||||
<h1 className="text-2xl font-bold text-[#404040] mb-4">관리자 홈</h1>
|
{/* 로고 */}
|
||||||
<p className="text-[#515151]">관리자 페이지에 오신 것을 환영합니다.</p>
|
<button
|
||||||
|
onClick={() => router.push('/')}
|
||||||
|
className="h-[102px] relative shrink-0 w-[99px] cursor-pointer hover:opacity-80 transition-opacity"
|
||||||
|
>
|
||||||
|
<div className="absolute inset-0 flex items-center justify-center overflow-hidden pointer-events-none">
|
||||||
|
<img alt="로고" className="h-full w-full object-contain" src="/logo.svg" />
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
{/* 메뉴 */}
|
||||||
|
<div className="box-border content-stretch flex flex-col items-center pb-0 pt-4 px-0 relative shrink-0 w-[250px]">
|
||||||
|
<div className="box-border content-stretch flex flex-col gap-2 items-start p-3 relative shrink-0 w-full">
|
||||||
|
<button className="bg-[#f7f7f7] box-border content-stretch flex gap-2 h-[34px] items-center pl-2 pr-2 py-1 relative rounded-lg shrink-0 w-[226px] cursor-pointer">
|
||||||
|
<div className="basis-0 content-stretch flex gap-2 grow items-center min-h-px min-w-px relative shrink-0 pl-2">
|
||||||
|
<p className="[white-space-collapse:collapse] basis-0 font-medium grow leading-[1.6] min-h-px min-w-px not-italic overflow-ellipsis overflow-hidden relative shrink-0 text-[14px] text-[#404040] text-left text-nowrap">
|
||||||
|
권한 설정
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => router.push('/admin_lecture1')}
|
||||||
|
className="box-border content-stretch flex gap-2 h-[34px] items-center pl-2 pr-2 py-1 relative rounded-lg shrink-0 w-[226px] cursor-pointer hover:bg-gray-50 transition-colors"
|
||||||
|
>
|
||||||
|
<div className="basis-0 content-stretch flex gap-2 grow items-center min-h-px min-w-px relative shrink-0 pl-2">
|
||||||
|
<p className="[white-space-collapse:collapse] basis-0 font-medium grow leading-[1.6] min-h-px min-w-px not-italic overflow-ellipsis overflow-hidden relative shrink-0 text-[14px] text-[#404040] text-left text-nowrap">
|
||||||
|
교육 과정 관리
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => router.push('/admin_lecture2')}
|
||||||
|
className="box-border content-stretch flex gap-2 h-[34px] items-center pl-2 pr-2 py-1 relative rounded-lg shrink-0 w-[226px] cursor-pointer hover:bg-gray-50 transition-colors"
|
||||||
|
>
|
||||||
|
<div className="basis-0 content-stretch flex gap-2 grow items-center min-h-px min-w-px relative shrink-0 pl-2">
|
||||||
|
<p className="[white-space-collapse:collapse] basis-0 font-medium grow leading-[1.6] min-h-px min-w-px not-italic overflow-ellipsis overflow-hidden relative shrink-0 text-[14px] text-[#404040] text-left text-nowrap">
|
||||||
|
강좌 관리
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
<button className="box-border content-stretch flex gap-2 h-[34px] items-center pl-2 pr-2 py-1 relative rounded-lg shrink-0 w-[226px] cursor-pointer hover:bg-gray-50 transition-colors">
|
||||||
|
<div className="basis-0 content-stretch flex gap-2 grow items-center min-h-px min-w-px relative shrink-0 pl-2">
|
||||||
|
<p className="[white-space-collapse:collapse] basis-0 font-medium grow leading-[1.6] min-h-px min-w-px not-italic overflow-ellipsis overflow-hidden relative shrink-0 text-[14px] text-[#404040] text-left text-nowrap">
|
||||||
|
문제 은행 관리
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
<button className="box-border content-stretch flex gap-2 h-[34px] items-center pl-2 pr-2 py-1 relative rounded-lg shrink-0 w-[226px] cursor-pointer hover:bg-gray-50 transition-colors">
|
||||||
|
<div className="basis-0 content-stretch flex gap-2 grow items-center min-h-px min-w-px relative shrink-0 pl-2">
|
||||||
|
<p className="[white-space-collapse:collapse] basis-0 font-medium grow leading-[1.6] min-h-px min-w-px not-italic overflow-ellipsis overflow-hidden relative shrink-0 text-[14px] text-[#404040] text-left text-nowrap">
|
||||||
|
수료증 발급
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
<button className="box-border content-stretch flex gap-2 h-[34px] items-center pl-2 pr-2 py-1 relative rounded-lg shrink-0 w-[226px] cursor-pointer hover:bg-gray-50 transition-colors">
|
||||||
|
<div className="basis-0 content-stretch flex gap-2 grow items-center min-h-px min-w-px relative shrink-0 pl-2">
|
||||||
|
<p className="[white-space-collapse:collapse] basis-0 font-medium grow leading-[1.6] min-h-px min-w-px not-italic overflow-ellipsis overflow-hidden relative shrink-0 text-[14px] text-[#404040] text-left text-nowrap">
|
||||||
|
공지사항
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
<button className="box-border content-stretch flex gap-2 h-[34px] items-center pl-2 pr-2 py-1 relative rounded-lg shrink-0 w-[226px] cursor-pointer hover:bg-gray-50 transition-colors">
|
||||||
|
<div className="basis-0 content-stretch flex gap-2 grow items-center min-h-px min-w-px relative shrink-0 pl-2">
|
||||||
|
<p className="[white-space-collapse:collapse] basis-0 font-medium grow leading-[1.6] min-h-px min-w-px not-italic overflow-ellipsis overflow-hidden relative shrink-0 text-[14px] text-[#404040] text-left text-nowrap">
|
||||||
|
학습 자료실
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
<button className="box-border content-stretch flex gap-2 h-[34px] items-center pl-2 pr-2 py-1 relative rounded-lg shrink-0 w-[226px] cursor-pointer hover:bg-gray-50 transition-colors">
|
||||||
|
<div className="basis-0 content-stretch flex gap-2 grow items-center min-h-px min-w-px relative shrink-0 pl-2">
|
||||||
|
<p className="[white-space-collapse:collapse] basis-0 font-medium grow leading-[1.6] min-h-px min-w-px not-italic overflow-ellipsis overflow-hidden relative shrink-0 text-[14px] text-[#404040] text-left text-nowrap">
|
||||||
|
로그/접속 기록
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
<button className="box-border content-stretch flex gap-2 h-[34px] items-center pl-2 pr-2 py-1 relative rounded-lg shrink-0 w-[226px] cursor-pointer hover:bg-gray-50 transition-colors">
|
||||||
|
<div className="basis-0 content-stretch flex gap-2 grow items-center min-h-px min-w-px relative shrink-0 pl-2">
|
||||||
|
<p className="[white-space-collapse:collapse] basis-0 font-medium grow leading-[1.6] min-h-px min-w-px not-italic overflow-ellipsis overflow-hidden relative shrink-0 text-[14px] text-[#404040] text-left text-nowrap">
|
||||||
|
배너 관리
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{/* 로그아웃 */}
|
||||||
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
localStorage.removeItem('isLoggedIn');
|
||||||
|
localStorage.removeItem('isAdminLoggedIn');
|
||||||
|
router.push('/login');
|
||||||
|
}}
|
||||||
|
className="content-stretch flex gap-[9px] h-[36px] items-center relative shrink-0 cursor-pointer hover:opacity-80 transition-opacity"
|
||||||
|
>
|
||||||
|
<div className="flex items-center justify-center relative shrink-0">
|
||||||
|
<div className="flex-none rotate-[180deg] scale-y-[-100%]">
|
||||||
|
<div className="h-[23.12px] relative w-[22px]">
|
||||||
|
<Logout />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="box-border content-stretch flex gap-[10px] items-center justify-center px-[10px] py-[5px] relative shrink-0">
|
||||||
|
<p className="font-medium leading-[1.6] not-italic relative shrink-0 text-[16px] text-[#404040] text-nowrap whitespace-pre">
|
||||||
|
로그아웃
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 메인 콘텐츠 */}
|
||||||
|
<div className="absolute left-[250px] top-0 right-0 min-h-screen">
|
||||||
|
{/* 페이지 타이틀 탭 */}
|
||||||
|
<div className="absolute content-stretch flex gap-[24px] items-center left-[48px] top-[45px]">
|
||||||
|
<button
|
||||||
|
onClick={() => setSelectedTab('전체')}
|
||||||
|
className={`border-b-[2px] border-solid box-border content-stretch flex gap-[150px] items-center relative shrink-0 ${selectedTab === '전체' ? 'border-[#2b82e8]' : 'border-transparent'}`}
|
||||||
|
>
|
||||||
|
<div className="box-border content-stretch flex gap-[10px] items-center justify-center p-[10px] relative shrink-0">
|
||||||
|
<p className="font-bold leading-[normal] not-italic relative shrink-0 text-[#515151] text-[18px] text-nowrap whitespace-pre">
|
||||||
|
전체
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => setSelectedTab('학습자')}
|
||||||
|
className={`border-b-[2px] border-solid box-border content-stretch flex gap-[150px] items-center relative shrink-0 ${selectedTab === '학습자' ? 'border-[#2b82e8]' : 'border-transparent'}`}
|
||||||
|
>
|
||||||
|
<div className="box-border content-stretch flex gap-[10px] items-center justify-center p-[10px] relative shrink-0">
|
||||||
|
<p className="font-bold leading-[normal] not-italic relative shrink-0 text-[#515151] text-[18px] text-nowrap whitespace-pre">
|
||||||
|
학습자
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => setSelectedTab('강사')}
|
||||||
|
className={`border-b-[2px] border-solid box-border content-stretch flex gap-[150px] items-center relative shrink-0 ${selectedTab === '강사' ? 'border-[#2b82e8]' : 'border-transparent'}`}
|
||||||
|
>
|
||||||
|
<div className="box-border content-stretch flex gap-[10px] items-center justify-center p-[10px] relative shrink-0">
|
||||||
|
<p className="font-bold leading-[normal] not-italic relative shrink-0 text-[#515151] text-[18px] text-nowrap whitespace-pre">
|
||||||
|
강사
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => setSelectedTab('운영자')}
|
||||||
|
className={`border-b-[2px] border-solid box-border content-stretch flex gap-[150px] items-center relative shrink-0 ${selectedTab === '운영자' ? 'border-[#2b82e8]' : 'border-transparent'}`}
|
||||||
|
>
|
||||||
|
<div className="box-border content-stretch flex gap-[10px] items-center justify-center p-[10px] relative shrink-0">
|
||||||
|
<p className="font-bold leading-[normal] not-italic relative shrink-0 text-[#515151] text-[18px] text-nowrap whitespace-pre">
|
||||||
|
운영자
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 사용자 목록 테이블 */}
|
||||||
|
<div className="absolute content-stretch flex flex-col items-start left-[48px] right-[101px] top-[135px] h-[671px] overflow-hidden">
|
||||||
|
{/* 테이블 헤더 */}
|
||||||
|
<div className="bg-[rgba(235,247,255,0.5)] content-stretch flex h-[41px] items-center relative shrink-0 w-full">
|
||||||
|
<div className="content-stretch flex items-center relative shrink-0 w-full">
|
||||||
|
<div className="basis-0 border-[0.5px_0px_0.5px_0.5px] border-[#b9b9b9] border-solid box-border content-stretch flex gap-[10px] grow items-center min-h-px min-w-px p-[10px] relative shrink-0">
|
||||||
|
<div className="basis-0 flex flex-col font-bold grow justify-center leading-[0] min-h-px min-w-px not-italic relative shrink-0 text-[18px] text-[#515151]">
|
||||||
|
<p className="leading-[normal]">가입일</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="basis-0 border-[0.5px_0px_0.5px_0.5px] border-[#b9b9b9] border-solid box-border content-stretch flex gap-[10px] grow items-center min-h-px min-w-px p-[10px] relative shrink-0">
|
||||||
|
<div className="basis-0 flex flex-col font-bold grow justify-center leading-[0] min-h-px min-w-px not-italic relative shrink-0 text-[18px] text-[#515151]">
|
||||||
|
<p className="leading-[normal]">사용자명</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="basis-0 border-[0.5px_0px_0.5px_0.5px] border-[#b9b9b9] border-solid box-border content-stretch flex gap-[10px] grow items-center min-h-px min-w-px p-[10px] relative shrink-0">
|
||||||
|
<div className="basis-0 flex flex-col font-bold grow justify-center leading-[0] min-h-px min-w-px not-italic relative shrink-0 text-[18px] text-[#515151]">
|
||||||
|
<p className="leading-[normal]">아이디(이메일)</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="basis-0 border-[0.5px_0px_0.5px_0.5px] border-[#b9b9b9] border-solid box-border content-stretch flex gap-[10px] grow items-center min-h-px min-w-px p-[10px] relative shrink-0">
|
||||||
|
<div className="basis-0 flex flex-col font-bold grow justify-center leading-[0] min-h-px min-w-px not-italic relative shrink-0 text-[18px] text-[#515151]">
|
||||||
|
<p className="leading-[normal]">권한</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="basis-0 border-[0.5px_0px_0.5px_0.5px] border-[#b9b9b9] border-solid box-border content-stretch flex gap-[10px] grow items-center min-h-px min-w-px p-[10px] relative shrink-0">
|
||||||
|
<div className="basis-0 flex flex-col font-bold grow justify-center leading-[0] min-h-px min-w-px not-italic relative shrink-0 text-[18px] text-[#515151]">
|
||||||
|
<p className="leading-[normal]">계정 상태</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="basis-0 border-[0.5px_0px_0.5px_0.5px] border-[#b9b9b9] border-solid box-border content-stretch flex gap-[10px] grow items-center min-h-px min-w-px p-[10px] relative shrink-0">
|
||||||
|
<div className="basis-0 flex flex-col font-bold grow justify-center leading-[0] min-h-px min-w-px not-italic relative shrink-0 text-[18px] text-[#515151]">
|
||||||
|
<p className="leading-[normal]">계정 관리</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 테이블 바디 */}
|
||||||
|
{users.length === 0 ? (
|
||||||
|
<div className="relative w-full flex items-center justify-center" style={{ minHeight: '400px' }}>
|
||||||
|
<div className="content-stretch flex flex-col gap-[16px] items-center relative shrink-0">
|
||||||
|
<div className="content-stretch flex flex-col gap-[2px] items-center relative shrink-0">
|
||||||
|
<p className="font-medium leading-[1.6] not-italic relative shrink-0 text-[16px] text-[#969696] text-nowrap whitespace-pre">
|
||||||
|
존재하는 계정이 없어요.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="content-stretch flex flex-col items-start relative shrink-0 w-full max-h-[630px] overflow-y-auto">
|
||||||
|
{currentUsers.map((user) => (
|
||||||
|
<div key={user.id} className="bg-white content-stretch flex items-center relative shrink-0 w-full">
|
||||||
|
<div className="content-stretch flex items-center relative shrink-0 w-full">
|
||||||
|
<div className="basis-0 border-[0.5px_0px_0.5px_0.5px] border-[#b9b9b9] border-solid box-border content-stretch flex gap-[10px] grow items-center min-h-px min-w-px p-[10px] relative shrink-0">
|
||||||
|
<div className="basis-0 flex flex-col font-medium grow justify-center leading-[0] min-h-px min-w-px not-italic relative shrink-0 text-[18px] text-[#515151]">
|
||||||
|
<p className="leading-[normal]">{user.joinDate}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="basis-0 border-[0.5px_0px_0.5px_0.5px] border-[#b9b9b9] border-solid box-border content-stretch flex gap-[10px] grow items-center min-h-px min-w-px p-[10px] relative shrink-0">
|
||||||
|
<div className="basis-0 flex flex-col font-medium grow justify-center leading-[0] min-h-px min-w-px not-italic relative shrink-0 text-[18px] text-[#515151]">
|
||||||
|
<p className="leading-[normal]">{user.name}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="basis-0 border-[0.5px_0px_0.5px_0.5px] border-[#b9b9b9] border-solid box-border content-stretch flex gap-[10px] grow items-center min-h-px min-w-px p-[10px] relative shrink-0">
|
||||||
|
<div className="basis-0 flex flex-col font-medium grow justify-center leading-[0] min-h-px min-w-px not-italic relative shrink-0 text-[18px] text-[#515151]">
|
||||||
|
<p className="leading-[normal]">{user.email}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="basis-0 border-[0.5px_0px_0.5px_0.5px] border-[#b9b9b9] border-solid box-border content-stretch flex gap-[10px] grow items-center min-h-px min-w-px p-[10px] relative shrink-0">
|
||||||
|
<div className="basis-0 flex flex-col font-medium grow justify-center leading-[0] min-h-px min-w-px not-italic relative shrink-0 text-[18px] text-[#515151]">
|
||||||
|
<p className="leading-[normal]">{user.role}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="basis-0 border-[0.5px_0px_0.5px_0.5px] border-[#b9b9b9] border-solid box-border content-stretch flex gap-[10px] grow items-center min-h-px min-w-px p-[10px] relative shrink-0">
|
||||||
|
<div className="basis-0 flex flex-col font-medium grow justify-center leading-[0] min-h-px min-w-px not-italic relative shrink-0 text-[18px] text-[#515151]">
|
||||||
|
<p className="leading-[normal]">{user.accountStatus}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="basis-0 border-[0.5px_0px_0.5px_0.5px] border-[#b9b9b9] border-solid box-border content-stretch flex gap-[10px] grow items-center min-h-px min-w-px p-[10px] relative shrink-0">
|
||||||
|
<div className="basis-0 flex flex-col font-medium grow justify-center leading-[0] min-h-px min-w-px not-italic relative shrink-0 text-[18px] text-[#515151]">
|
||||||
|
<p className="leading-[normal]">{user.accountManagement}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 페이지네이션 */}
|
||||||
|
{users.length > 0 && (
|
||||||
|
<div className="absolute content-stretch flex gap-2 items-center justify-center left-[48px] right-[101px] top-[909px]">
|
||||||
|
<button onClick={() => setCurrentPage(Math.max(1, currentPage - 1))}>
|
||||||
|
<PaginationBtnMove status={currentPage === 1 ? "Disabled" : "Default"} move="Previous" />
|
||||||
|
</button>
|
||||||
|
<div className="content-stretch flex gap-2 items-center relative shrink-0">
|
||||||
|
{pageNumbers.map((pageNum) => (
|
||||||
|
<button
|
||||||
|
key={pageNum}
|
||||||
|
onClick={() => setCurrentPage(pageNum)}
|
||||||
|
className={`relative rounded-[8px] shrink-0 size-[30px] cursor-pointer transition-colors ${currentPage === pageNum
|
||||||
|
? 'bg-[#1d1d1d]'
|
||||||
|
: 'border border-[#eeeeee] border-solid hover:bg-gray-50'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<div className="overflow-clip relative rounded-[inherit] size-[30px]">
|
||||||
|
<div className={`absolute flex flex-col font-medium justify-center leading-[0] left-[15.5px] not-italic text-[14px] text-center text-nowrap top-[15px] translate-x-[-50%] translate-y-[-50%] ${currentPage === pageNum ? 'text-white' : 'text-[#515151]'
|
||||||
|
}`}>
|
||||||
|
<p className="leading-[1.6] whitespace-pre">{pageNum}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
<button onClick={() => setCurrentPage(Math.min(totalPages, currentPage + 1))}>
|
||||||
|
<PaginationBtnMove status={currentPage === totalPages ? "Disabled" : "Default"} move="Next" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -277,7 +277,10 @@ export default function AdminLecture1Page() {
|
|||||||
{/* 메뉴 */}
|
{/* 메뉴 */}
|
||||||
<div className="box-border content-stretch flex flex-col items-center pb-0 pt-4 px-0 relative shrink-0 w-[250px]">
|
<div className="box-border content-stretch flex flex-col items-center pb-0 pt-4 px-0 relative shrink-0 w-[250px]">
|
||||||
<div className="box-border content-stretch flex flex-col gap-2 items-start p-3 relative shrink-0 w-full">
|
<div className="box-border content-stretch flex flex-col gap-2 items-start p-3 relative shrink-0 w-full">
|
||||||
<button className="box-border content-stretch flex gap-2 h-[34px] items-center pl-2 pr-2 py-1 relative rounded-lg shrink-0 w-[226px] cursor-pointer hover:bg-gray-50 transition-colors">
|
<button
|
||||||
|
onClick={() => router.push('/admin_home')}
|
||||||
|
className="box-border content-stretch flex gap-2 h-[34px] items-center pl-2 pr-2 py-1 relative rounded-lg shrink-0 w-[226px] cursor-pointer hover:bg-gray-50 transition-colors"
|
||||||
|
>
|
||||||
<div className="basis-0 content-stretch flex gap-2 grow items-center min-h-px min-w-px relative shrink-0 pl-2">
|
<div className="basis-0 content-stretch flex gap-2 grow items-center min-h-px min-w-px relative shrink-0 pl-2">
|
||||||
<p className="[white-space-collapse:collapse] basis-0 font-medium grow leading-[1.6] min-h-px min-w-px not-italic overflow-ellipsis overflow-hidden relative shrink-0 text-[14px] text-[#404040] text-left text-nowrap">
|
<p className="[white-space-collapse:collapse] basis-0 font-medium grow leading-[1.6] min-h-px min-w-px not-italic overflow-ellipsis overflow-hidden relative shrink-0 text-[14px] text-[#404040] text-left text-nowrap">
|
||||||
권한 설정
|
권한 설정
|
||||||
@@ -346,7 +349,14 @@ export default function AdminLecture1Page() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/* 로그아웃 */}
|
{/* 로그아웃 */}
|
||||||
<div className="content-stretch flex gap-[9px] items-center relative shrink-0">
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
localStorage.removeItem('isLoggedIn');
|
||||||
|
localStorage.removeItem('isAdminLoggedIn');
|
||||||
|
router.push('/login');
|
||||||
|
}}
|
||||||
|
className="content-stretch flex gap-[9px] h-[36px] items-center relative shrink-0 cursor-pointer hover:opacity-80 transition-opacity"
|
||||||
|
>
|
||||||
<div className="flex items-center justify-center relative shrink-0">
|
<div className="flex items-center justify-center relative shrink-0">
|
||||||
<div className="flex-none rotate-[180deg] scale-y-[-100%]">
|
<div className="flex-none rotate-[180deg] scale-y-[-100%]">
|
||||||
<div className="h-[23.12px] relative w-[22px]">
|
<div className="h-[23.12px] relative w-[22px]">
|
||||||
@@ -354,21 +364,12 @@ export default function AdminLecture1Page() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button
|
<div className="box-border content-stretch flex gap-[10px] items-center justify-center px-[10px] py-[5px] relative shrink-0">
|
||||||
onClick={() => {
|
<p className="font-medium leading-[1.6] not-italic relative shrink-0 text-[16px] text-[#404040] text-nowrap whitespace-pre">
|
||||||
localStorage.removeItem('isLoggedIn');
|
로그아웃
|
||||||
localStorage.removeItem('isAdminLoggedIn');
|
</p>
|
||||||
router.push('/');
|
</div>
|
||||||
}}
|
</button>
|
||||||
className="content-stretch flex h-[36px] items-center justify-between relative shrink-0 w-[76px] cursor-pointer hover:opacity-80 transition-opacity"
|
|
||||||
>
|
|
||||||
<div className="box-border content-stretch flex gap-[10px] items-center justify-center px-[10px] py-[5px] relative shrink-0">
|
|
||||||
<p className="font-medium leading-[1.6] not-italic relative shrink-0 text-[16px] text-[#404040] text-nowrap whitespace-pre">
|
|
||||||
로그아웃
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* 메인 콘텐츠 */}
|
{/* 메인 콘텐츠 */}
|
||||||
|
|||||||
@@ -6,6 +6,104 @@ import { isAdminLoggedIn } from '../../lib/auth';
|
|||||||
import LoginPage from '../login/page';
|
import LoginPage from '../login/page';
|
||||||
import Logout from '../../public/svg/logout';
|
import Logout from '../../public/svg/logout';
|
||||||
|
|
||||||
|
const imgArrowDisabled = "http://localhost:3845/assets/6edcb2defc36a2bf4a05a3abe53b8da3d42b2cb4.svg";
|
||||||
|
const imgArrowDefault = "http://localhost:3845/assets/ad0cb4418492f1b020bb38a2ff038a331294ce87.svg";
|
||||||
|
const imgArrowNext = "http://localhost:3845/assets/6328cf96ee1169c1425c2ce55e7a2dcca0374508.svg";
|
||||||
|
|
||||||
|
type PaginationMove = "Previous" | "Next";
|
||||||
|
type PaginationStatus = "Default" | "Disabled";
|
||||||
|
|
||||||
|
function PaginationBtnMove({ status = "Default", move = "Previous" }: { status?: PaginationStatus; move?: PaginationMove }) {
|
||||||
|
const isDisabled = status === "Disabled";
|
||||||
|
const isNext = move === "Next";
|
||||||
|
|
||||||
|
if (isDisabled && isNext) {
|
||||||
|
return (
|
||||||
|
<div className="border border-[#eeeeee] border-solid relative rounded-[8px] shrink-0 size-[30px]">
|
||||||
|
<div className="overflow-clip relative rounded-[inherit] size-[30px]">
|
||||||
|
<div className="absolute flex items-center justify-center left-[5px] size-[20px] top-[5px]">
|
||||||
|
<div className="flex-none rotate-[180deg]">
|
||||||
|
<div className="overflow-clip relative size-[20px]">
|
||||||
|
<div className="absolute flex inset-[15.63%_30.21%] items-center justify-center">
|
||||||
|
<div className="flex-none h-[16.5px] rotate-[180deg] w-[9.5px]">
|
||||||
|
<div className="relative size-full">
|
||||||
|
<div className="absolute inset-0">
|
||||||
|
<img alt="" className="block max-w-none size-full" style={{ filter: 'opacity(0.88) brightness(0) saturate(100%) invert(88%)', opacity: 1 }} src={imgArrowNext} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isDisabled && !isNext) {
|
||||||
|
return (
|
||||||
|
<div className="border border-[#eeeeee] border-solid relative rounded-[8px] shrink-0 size-[30px]">
|
||||||
|
<div className="overflow-clip relative rounded-[inherit] size-[30px]">
|
||||||
|
<div className="absolute flex items-center justify-center left-[5px] size-[20px] top-[5px]">
|
||||||
|
<div className="flex-none rotate-[180deg]">
|
||||||
|
<div className="overflow-clip relative size-[20px]">
|
||||||
|
<div className="absolute inset-[15.63%_30.21%]">
|
||||||
|
<div className="absolute inset-0">
|
||||||
|
<img alt="" className="block max-w-none size-full" style={{ filter: 'opacity(0.88) brightness(0) saturate(100%) invert(88%)', opacity: 1 }} src={imgArrowDefault} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isNext) {
|
||||||
|
return (
|
||||||
|
<div className="border border-[#eeeeee] border-solid relative rounded-[8px] shrink-0 size-[30px] cursor-pointer hover:bg-gray-50 transition-colors">
|
||||||
|
<div className="overflow-clip relative rounded-[inherit] size-[30px]">
|
||||||
|
<div className="absolute flex items-center justify-center left-[5px] size-[20px] top-[5px]">
|
||||||
|
<div className="flex-none rotate-[180deg]">
|
||||||
|
<div className="overflow-clip relative size-[20px]">
|
||||||
|
<div className="absolute flex inset-[15.63%_30.21%] items-center justify-center">
|
||||||
|
<div className="flex-none h-[16.5px] rotate-[180deg] w-[9.5px]">
|
||||||
|
<div className="relative size-full">
|
||||||
|
<div className="absolute inset-0">
|
||||||
|
<img alt="" className="block max-w-none size-full" src={imgArrowNext} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="border border-[#eeeeee] border-solid relative rounded-[8px] shrink-0 size-[30px] cursor-pointer hover:bg-gray-50 transition-colors">
|
||||||
|
<div className="overflow-clip relative rounded-[inherit] size-[30px]">
|
||||||
|
<div className="absolute flex items-center justify-center left-[5px] size-[20px] top-[5px]">
|
||||||
|
<div className="flex-none rotate-[180deg]">
|
||||||
|
<div className="overflow-clip relative size-[20px]">
|
||||||
|
<div className="absolute inset-[15.63%_30.21%]">
|
||||||
|
<div className="absolute inset-0">
|
||||||
|
<img alt="" className="block max-w-none size-full" src={imgArrowDefault} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
interface Lecture {
|
interface Lecture {
|
||||||
id: number;
|
id: number;
|
||||||
courseName: string;
|
courseName: string;
|
||||||
@@ -20,7 +118,29 @@ export default function AdminLecture2Page() {
|
|||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const [isLoading, setIsLoading] = useState(true);
|
const [isLoading, setIsLoading] = useState(true);
|
||||||
const [isAuthenticated, setIsAuthenticated] = useState(false);
|
const [isAuthenticated, setIsAuthenticated] = useState(false);
|
||||||
const [lectures, setLectures] = useState<Lecture[]>([]);
|
const [currentPage, setCurrentPage] = useState(1);
|
||||||
|
const [lectures, setLectures] = useState<Lecture[]>([
|
||||||
|
{ id: 39, courseName: '방사선 적용', lectureName: '방사선 기본 원리', attachedFile: '파일1.pdf', questionCount: 10, registrar: '이철수', createdAt: '2026-01-15' },
|
||||||
|
{ id: 38, courseName: '방사선 적용', lectureName: '방사선 안전 관리', attachedFile: '파일2.pdf', questionCount: 15, registrar: '이철수', createdAt: '2026-01-14' },
|
||||||
|
{ id: 37, courseName: '방사선 원리', lectureName: '방사선 측정 기법', attachedFile: '파일3.pdf', questionCount: 12, registrar: '김등록', createdAt: '2026-01-13' },
|
||||||
|
{ id: 36, courseName: '방사선 적용', lectureName: '방사선 장비 사용법', attachedFile: '파일4.pdf', questionCount: 8, registrar: '이철수', createdAt: '2026-01-12' },
|
||||||
|
{ id: 35, courseName: '방사선 원리', lectureName: '방사선 물리학 기초', attachedFile: '파일5.pdf', questionCount: 20, registrar: '김등록', createdAt: '2026-01-11' },
|
||||||
|
{ id: 34, courseName: '방사선 적용', lectureName: '방사선 보호 장비', attachedFile: '파일6.pdf', questionCount: 14, registrar: '이철수', createdAt: '2026-01-10' },
|
||||||
|
{ id: 33, courseName: '방사선 적용', lectureName: '방사선 응용 실습', attachedFile: '파일7.pdf', questionCount: 16, registrar: '이철수', createdAt: '2026-01-09' },
|
||||||
|
{ id: 32, courseName: '방사선 원리', lectureName: '방사선 화학 반응', attachedFile: '파일8.pdf', questionCount: 11, registrar: '김등록', createdAt: '2026-01-08' },
|
||||||
|
{ id: 31, courseName: '방사선 적용', lectureName: '방사선 의료 응용', attachedFile: '파일9.pdf', questionCount: 18, registrar: '이철수', createdAt: '2026-01-07' },
|
||||||
|
{ id: 30, courseName: '방사선 적용', lectureName: '방사선 산업 응용', attachedFile: '파일10.pdf', questionCount: 13, registrar: '이철수', createdAt: '2026-01-06' },
|
||||||
|
{ id: 29, courseName: '방사선 원리', lectureName: '방사선 생물학', attachedFile: '파일11.pdf', questionCount: 17, registrar: '김등록', createdAt: '2026-01-05' },
|
||||||
|
{ id: 28, courseName: '방사선 적용', lectureName: '방사선 환경 모니터링', attachedFile: '파일12.pdf', questionCount: 9, registrar: '이철수', createdAt: '2026-01-04' },
|
||||||
|
{ id: 27, courseName: '방사선 적용', lectureName: '방사선 검사 기법', attachedFile: '파일13.pdf', questionCount: 19, registrar: '이철수', createdAt: '2026-01-03' },
|
||||||
|
{ id: 26, courseName: '방사선 원리', lectureName: '방사선 에너지 전달', attachedFile: '파일14.pdf', questionCount: 21, registrar: '김등록', createdAt: '2026-01-02' },
|
||||||
|
{ id: 25, courseName: '방사선 적용', lectureName: '방사선 처리 기술', attachedFile: '파일15.pdf', questionCount: 7, registrar: '이철수', createdAt: '2026-01-01' },
|
||||||
|
{ id: 24, courseName: '방사선 적용', lectureName: '방사선 품질 관리', attachedFile: '파일16.pdf', questionCount: 22, registrar: '이철수', createdAt: '2025-12-31' },
|
||||||
|
{ id: 23, courseName: '방사선 원리', lectureName: '방사선 방어 이론', attachedFile: '파일17.pdf', questionCount: 6, registrar: '김등록', createdAt: '2025-12-30' },
|
||||||
|
{ id: 22, courseName: '방사선 적용', lectureName: '방사선 계측법', attachedFile: '파일18.pdf', questionCount: 24, registrar: '이철수', createdAt: '2025-12-29' },
|
||||||
|
{ id: 21, courseName: '방사선 적용', lectureName: '방사선 안전 규정', attachedFile: '파일19.pdf', questionCount: 5, registrar: '이철수', createdAt: '2025-12-28' },
|
||||||
|
{ id: 20, courseName: '방사선 원리', lectureName: '방사선 핵물리학', attachedFile: '파일20.pdf', questionCount: 25, registrar: '김등록', createdAt: '2025-12-27' },
|
||||||
|
]);
|
||||||
|
|
||||||
// 관리자 인증 확인
|
// 관리자 인증 확인
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -48,6 +168,13 @@ export default function AdminLecture2Page() {
|
|||||||
return <LoginPage />;
|
return <LoginPage />;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const itemsPerPage = 13;
|
||||||
|
const totalPages = Math.ceil(lectures.length / itemsPerPage);
|
||||||
|
const pageNumbers = Array.from({ length: totalPages }, (_, i) => i + 1);
|
||||||
|
const startIndex = (currentPage - 1) * itemsPerPage;
|
||||||
|
const endIndex = startIndex + itemsPerPage;
|
||||||
|
const currentLectures = lectures.slice(startIndex, endIndex);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="bg-white relative size-full min-h-screen">
|
<div className="bg-white relative size-full min-h-screen">
|
||||||
{/* 사이드바 */}
|
{/* 사이드바 */}
|
||||||
@@ -64,7 +191,10 @@ export default function AdminLecture2Page() {
|
|||||||
{/* 메뉴 */}
|
{/* 메뉴 */}
|
||||||
<div className="box-border content-stretch flex flex-col items-center pb-0 pt-4 px-0 relative shrink-0 w-[250px]">
|
<div className="box-border content-stretch flex flex-col items-center pb-0 pt-4 px-0 relative shrink-0 w-[250px]">
|
||||||
<div className="box-border content-stretch flex flex-col gap-2 items-start p-3 relative shrink-0 w-full">
|
<div className="box-border content-stretch flex flex-col gap-2 items-start p-3 relative shrink-0 w-full">
|
||||||
<button className="box-border content-stretch flex gap-2 h-[34px] items-center pl-2 pr-2 py-1 relative rounded-lg shrink-0 w-[226px] cursor-pointer hover:bg-gray-50 transition-colors">
|
<button
|
||||||
|
onClick={() => router.push('/admin_home')}
|
||||||
|
className="box-border content-stretch flex gap-2 h-[34px] items-center pl-2 pr-2 py-1 relative rounded-lg shrink-0 w-[226px] cursor-pointer hover:bg-gray-50 transition-colors"
|
||||||
|
>
|
||||||
<div className="basis-0 content-stretch flex gap-2 grow items-center min-h-px min-w-px relative shrink-0 pl-2">
|
<div className="basis-0 content-stretch flex gap-2 grow items-center min-h-px min-w-px relative shrink-0 pl-2">
|
||||||
<p className="[white-space-collapse:collapse] basis-0 font-medium grow leading-[1.6] min-h-px min-w-px not-italic overflow-ellipsis overflow-hidden relative shrink-0 text-[14px] text-[#404040] text-left text-nowrap">
|
<p className="[white-space-collapse:collapse] basis-0 font-medium grow leading-[1.6] min-h-px min-w-px not-italic overflow-ellipsis overflow-hidden relative shrink-0 text-[14px] text-[#404040] text-left text-nowrap">
|
||||||
권한 설정
|
권한 설정
|
||||||
@@ -133,7 +263,14 @@ export default function AdminLecture2Page() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/* 로그아웃 */}
|
{/* 로그아웃 */}
|
||||||
<div className="content-stretch flex gap-[9px] items-center relative shrink-0">
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
localStorage.removeItem('isLoggedIn');
|
||||||
|
localStorage.removeItem('isAdminLoggedIn');
|
||||||
|
router.push('/login');
|
||||||
|
}}
|
||||||
|
className="content-stretch flex gap-[9px] h-[36px] items-center relative shrink-0 cursor-pointer hover:opacity-80 transition-opacity"
|
||||||
|
>
|
||||||
<div className="flex items-center justify-center relative shrink-0">
|
<div className="flex items-center justify-center relative shrink-0">
|
||||||
<div className="flex-none rotate-[180deg] scale-y-[-100%]">
|
<div className="flex-none rotate-[180deg] scale-y-[-100%]">
|
||||||
<div className="h-[23.12px] relative w-[22px]">
|
<div className="h-[23.12px] relative w-[22px]">
|
||||||
@@ -141,21 +278,12 @@ export default function AdminLecture2Page() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button
|
<div className="box-border content-stretch flex gap-[10px] items-center justify-center px-[10px] py-[5px] relative shrink-0">
|
||||||
onClick={() => {
|
<p className="font-medium leading-[1.6] not-italic relative shrink-0 text-[16px] text-[#404040] text-nowrap whitespace-pre">
|
||||||
localStorage.removeItem('isLoggedIn');
|
로그아웃
|
||||||
localStorage.removeItem('isAdminLoggedIn');
|
</p>
|
||||||
router.push('/');
|
</div>
|
||||||
}}
|
</button>
|
||||||
className="content-stretch flex h-[36px] items-center justify-between relative shrink-0 w-[76px] cursor-pointer hover:opacity-80 transition-opacity"
|
|
||||||
>
|
|
||||||
<div className="box-border content-stretch flex gap-[10px] items-center justify-center px-[10px] py-[5px] relative shrink-0">
|
|
||||||
<p className="font-medium leading-[1.6] not-italic relative shrink-0 text-[16px] text-[#404040] text-nowrap whitespace-pre">
|
|
||||||
로그아웃
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* 메인 콘텐츠 */}
|
{/* 메인 콘텐츠 */}
|
||||||
@@ -181,7 +309,7 @@ export default function AdminLecture2Page() {
|
|||||||
</button>
|
</button>
|
||||||
|
|
||||||
{/* 테이블 */}
|
{/* 테이블 */}
|
||||||
<div className="absolute content-stretch flex flex-col items-start left-[48px] right-[101px] top-[186px]">
|
<div className="absolute content-stretch flex flex-col items-start left-[48px] right-[101px] top-[186px] h-[671px] overflow-hidden">
|
||||||
{/* 테이블 헤더 */}
|
{/* 테이블 헤더 */}
|
||||||
<div className="bg-[rgba(235,247,255,0.5)] content-stretch flex h-[41px] items-center relative shrink-0 w-full">
|
<div className="bg-[rgba(235,247,255,0.5)] content-stretch flex h-[41px] items-center relative shrink-0 w-full">
|
||||||
<div className="content-stretch flex items-center relative shrink-0 w-full">
|
<div className="content-stretch flex items-center relative shrink-0 w-full">
|
||||||
@@ -218,7 +346,7 @@ export default function AdminLecture2Page() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* 빈 상태 또는 테이블 바디 */}
|
{/* 테이블 바디 */}
|
||||||
{lectures.length === 0 ? (
|
{lectures.length === 0 ? (
|
||||||
<div className="relative w-full flex items-center justify-center" style={{ minHeight: '400px' }}>
|
<div className="relative w-full flex items-center justify-center" style={{ minHeight: '400px' }}>
|
||||||
<div className="content-stretch flex flex-col gap-[16px] items-center relative shrink-0">
|
<div className="content-stretch flex flex-col gap-[16px] items-center relative shrink-0">
|
||||||
@@ -230,8 +358,8 @@ export default function AdminLecture2Page() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="content-stretch flex flex-col items-start relative shrink-0 w-full">
|
<div className="content-stretch flex flex-col items-start relative shrink-0 w-full max-h-[630px] overflow-y-auto">
|
||||||
{lectures.map((lecture) => (
|
{currentLectures.map((lecture) => (
|
||||||
<div key={lecture.id} className="bg-white content-stretch flex items-center relative shrink-0 w-full">
|
<div key={lecture.id} className="bg-white content-stretch flex items-center relative shrink-0 w-full">
|
||||||
<div className="content-stretch flex items-center relative shrink-0 w-full">
|
<div className="content-stretch flex items-center relative shrink-0 w-full">
|
||||||
<div className="basis-0 border-[0.5px_0px_0.5px_0.5px] border-[#b9b9b9] border-solid box-border content-stretch flex gap-[10px] grow items-center min-h-px min-w-px p-[10px] relative shrink-0">
|
<div className="basis-0 border-[0.5px_0px_0.5px_0.5px] border-[#b9b9b9] border-solid box-border content-stretch flex gap-[10px] grow items-center min-h-px min-w-px p-[10px] relative shrink-0">
|
||||||
@@ -270,6 +398,37 @@ export default function AdminLecture2Page() {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* 페이지네이션 */}
|
||||||
|
{lectures.length > 0 && (
|
||||||
|
<div className="absolute content-stretch flex gap-2 items-center justify-center left-[48px] right-[101px] top-[909px]">
|
||||||
|
<button onClick={() => setCurrentPage(Math.max(1, currentPage - 1))}>
|
||||||
|
<PaginationBtnMove status={currentPage === 1 ? "Disabled" : "Default"} move="Previous" />
|
||||||
|
</button>
|
||||||
|
<div className="content-stretch flex gap-2 items-center relative shrink-0">
|
||||||
|
{pageNumbers.map((pageNum) => (
|
||||||
|
<button
|
||||||
|
key={pageNum}
|
||||||
|
onClick={() => setCurrentPage(pageNum)}
|
||||||
|
className={`relative rounded-[8px] shrink-0 size-[30px] cursor-pointer transition-colors ${currentPage === pageNum
|
||||||
|
? 'bg-[#1d1d1d]'
|
||||||
|
: 'border border-[#eeeeee] border-solid hover:bg-gray-50'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<div className="overflow-clip relative rounded-[inherit] size-[30px]">
|
||||||
|
<div className={`absolute flex flex-col font-medium justify-center leading-[0] left-[15.5px] not-italic text-[14px] text-center text-nowrap top-[15px] translate-x-[-50%] translate-y-[-50%] ${currentPage === pageNum ? 'text-white' : 'text-[#515151]'
|
||||||
|
}`}>
|
||||||
|
<p className="leading-[1.6] whitespace-pre">{pageNum}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
<button onClick={() => setCurrentPage(Math.min(totalPages, currentPage + 1))}>
|
||||||
|
<PaginationBtnMove status={currentPage === totalPages ? "Disabled" : "Default"} move="Next" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ import Image from 'next/image';
|
|||||||
import { useState, useEffect } from 'react';
|
import { useState, useEffect } from 'react';
|
||||||
import { useRouter } from 'next/navigation';
|
import { useRouter } from 'next/navigation';
|
||||||
import logo from '../logo.svg';
|
import logo from '../logo.svg';
|
||||||
|
import CheckboxOff from '../../public/svg/checkbox_off';
|
||||||
const checkIcon = "http://localhost:3845/assets/68720b08a673d8b68ae6482d642eeab286c9462b.svg";
|
import CheckboxOn from '../../public/svg/checkbox_on';
|
||||||
|
|
||||||
type CheckboxProps = {
|
type CheckboxProps = {
|
||||||
checked: boolean;
|
checked: boolean;
|
||||||
@@ -20,18 +20,9 @@ function Checkbox({ checked, onChange, label }: CheckboxProps) {
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={onChange}
|
onClick={onChange}
|
||||||
className="relative w-[18px] h-[18px] rounded-[4px]"
|
className="relative w-[18px] h-[18px] flex items-center justify-center"
|
||||||
>
|
>
|
||||||
{checked ? (
|
{checked ? <CheckboxOn /> : <CheckboxOff />}
|
||||||
<>
|
|
||||||
<div className="absolute bg-[#515151] left-0 rounded-[4px] w-[18px] h-[18px] top-0" />
|
|
||||||
<div className="absolute left-[3px] w-3 h-3 top-[3px]">
|
|
||||||
<img alt="" className="block max-w-none w-full h-full" src={checkIcon} />
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
<div className="absolute border border-[#b9b9b9] border-solid left-0 rounded-[4px] w-[18px] h-[18px] top-0" />
|
|
||||||
)}
|
|
||||||
</button>
|
</button>
|
||||||
<span className="text-sm text-[#515151] leading-[1.6]">{label}</span>
|
<span className="text-sm text-[#515151] leading-[1.6]">{label}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
223
app/page.tsx
223
app/page.tsx
@@ -6,7 +6,6 @@ import Image from 'next/image';
|
|||||||
import { isAdminLoggedIn } from '../lib/auth';
|
import { isAdminLoggedIn } from '../lib/auth';
|
||||||
import LoginPage from './login/page';
|
import LoginPage from './login/page';
|
||||||
import Header from './components/Header';
|
import Header from './components/Header';
|
||||||
import Logout from '../public/svg/logout';
|
|
||||||
|
|
||||||
const imgImage2 = "http://localhost:3845/assets/89fda8e949171025b1232bae70fc9d442e4e70c8.png";
|
const imgImage2 = "http://localhost:3845/assets/89fda8e949171025b1232bae70fc9d442e4e70c8.png";
|
||||||
const imgImage7 = "http://localhost:3845/assets/a4e4d09643b890b56084560cc24d6e532a03487b.png";
|
const imgImage7 = "http://localhost:3845/assets/a4e4d09643b890b56084560cc24d6e532a03487b.png";
|
||||||
@@ -21,7 +20,6 @@ export default function HomePage() {
|
|||||||
const [isAdmin, setIsAdmin] = useState(false);
|
const [isAdmin, setIsAdmin] = useState(false);
|
||||||
const [isLoading, setIsLoading] = useState(true);
|
const [isLoading, setIsLoading] = useState(true);
|
||||||
const [currentHeroSlide, setCurrentHeroSlide] = useState(0);
|
const [currentHeroSlide, setCurrentHeroSlide] = useState(0);
|
||||||
const [selectedTab, setSelectedTab] = useState<'전체' | '학습자' | '강사' | '운영자'>('전체');
|
|
||||||
|
|
||||||
// 임시 데이터 - 실제로는 API에서 가져올 데이터
|
// 임시 데이터 - 실제로는 API에서 가져올 데이터
|
||||||
const [courses, setCourses] = useState([
|
const [courses, setCourses] = useState([
|
||||||
@@ -61,7 +59,12 @@ export default function HomePage() {
|
|||||||
setIsLoggedIn(loginStatus);
|
setIsLoggedIn(loginStatus);
|
||||||
setIsAdmin(adminStatus);
|
setIsAdmin(adminStatus);
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
}, []);
|
|
||||||
|
// 관리자일 경우 admin_home 페이지로 리다이렉트
|
||||||
|
if (adminStatus) {
|
||||||
|
router.push('/admin_home');
|
||||||
|
}
|
||||||
|
}, [router]);
|
||||||
|
|
||||||
if (isLoading) {
|
if (isLoading) {
|
||||||
return null; // 로딩 중
|
return null; // 로딩 중
|
||||||
@@ -72,219 +75,9 @@ export default function HomePage() {
|
|||||||
return <LoginPage />;
|
return <LoginPage />;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 관리자일 경우 권한 설정 페이지 표시
|
// 관리자일 경우 리다이렉트 중
|
||||||
if (isAdmin) {
|
if (isAdmin) {
|
||||||
return (
|
return null;
|
||||||
<div className="bg-white relative size-full min-h-screen">
|
|
||||||
{/* 사이드바 */}
|
|
||||||
<div className="absolute bg-white border-r border-[#eeeeee] border-solid box-border content-stretch flex flex-col gap-[45px] items-center left-0 min-h-[1080px] pb-8 pt-[30px] px-0 top-0 w-[250px]">
|
|
||||||
{/* 로고 */}
|
|
||||||
<button
|
|
||||||
onClick={() => router.push('/')}
|
|
||||||
className="h-[102px] relative shrink-0 w-[99px] cursor-pointer hover:opacity-80 transition-opacity"
|
|
||||||
>
|
|
||||||
<div className="absolute inset-0 flex items-center justify-center overflow-hidden pointer-events-none">
|
|
||||||
<img alt="로고" className="h-full w-full object-contain" src="/logo.svg" />
|
|
||||||
</div>
|
|
||||||
</button>
|
|
||||||
{/* 메뉴 */}
|
|
||||||
<div className="box-border content-stretch flex flex-col items-center pb-0 pt-4 px-0 relative shrink-0 w-[250px]">
|
|
||||||
<div className="box-border content-stretch flex flex-col gap-2 items-start p-3 relative shrink-0 w-full">
|
|
||||||
<button className="bg-[#f7f7f7] box-border content-stretch flex gap-2 h-[34px] items-center pl-2 pr-2 py-1 relative rounded-lg shrink-0 w-[226px] cursor-pointer">
|
|
||||||
<div className="basis-0 content-stretch flex gap-2 grow items-center min-h-px min-w-px relative shrink-0 pl-2">
|
|
||||||
<p className="[white-space-collapse:collapse] basis-0 font-medium grow leading-[1.6] min-h-px min-w-px not-italic overflow-ellipsis overflow-hidden relative shrink-0 text-[14px] text-[#404040] text-left text-nowrap">
|
|
||||||
권한 설정
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
onClick={() => router.push('/admin_lecture1')}
|
|
||||||
className="box-border content-stretch flex gap-2 h-[34px] items-center pl-2 pr-2 py-1 relative rounded-lg shrink-0 w-[226px] cursor-pointer hover:bg-gray-50 transition-colors"
|
|
||||||
>
|
|
||||||
<div className="basis-0 content-stretch flex gap-2 grow items-center min-h-px min-w-px relative shrink-0 pl-2">
|
|
||||||
<p className="[white-space-collapse:collapse] basis-0 font-medium grow leading-[1.6] min-h-px min-w-px not-italic overflow-ellipsis overflow-hidden relative shrink-0 text-[14px] text-[#404040] text-left text-nowrap">
|
|
||||||
교육 과정 관리
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
onClick={() => router.push('/admin_lecture2')}
|
|
||||||
className="box-border content-stretch flex gap-2 h-[34px] items-center pl-2 pr-2 py-1 relative rounded-lg shrink-0 w-[226px] cursor-pointer hover:bg-gray-50 transition-colors"
|
|
||||||
>
|
|
||||||
<div className="basis-0 content-stretch flex gap-2 grow items-center min-h-px min-w-px relative shrink-0 pl-2">
|
|
||||||
<p className="[white-space-collapse:collapse] basis-0 font-medium grow leading-[1.6] min-h-px min-w-px not-italic overflow-ellipsis overflow-hidden relative shrink-0 text-[14px] text-[#404040] text-left text-nowrap">
|
|
||||||
강좌 관리
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</button>
|
|
||||||
<button className="box-border content-stretch flex gap-2 h-[34px] items-center pl-2 pr-2 py-1 relative rounded-lg shrink-0 w-[226px] cursor-pointer hover:bg-gray-50 transition-colors">
|
|
||||||
<div className="basis-0 content-stretch flex gap-2 grow items-center min-h-px min-w-px relative shrink-0 pl-2">
|
|
||||||
<p className="[white-space-collapse:collapse] basis-0 font-medium grow leading-[1.6] min-h-px min-w-px not-italic overflow-ellipsis overflow-hidden relative shrink-0 text-[14px] text-[#404040] text-left text-nowrap">
|
|
||||||
문제 은행 관리
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</button>
|
|
||||||
<button className="box-border content-stretch flex gap-2 h-[34px] items-center pl-2 pr-2 py-1 relative rounded-lg shrink-0 w-[226px] cursor-pointer hover:bg-gray-50 transition-colors">
|
|
||||||
<div className="basis-0 content-stretch flex gap-2 grow items-center min-h-px min-w-px relative shrink-0 pl-2">
|
|
||||||
<p className="[white-space-collapse:collapse] basis-0 font-medium grow leading-[1.6] min-h-px min-w-px not-italic overflow-ellipsis overflow-hidden relative shrink-0 text-[14px] text-[#404040] text-left text-nowrap">
|
|
||||||
수료증 발급
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</button>
|
|
||||||
<button className="box-border content-stretch flex gap-2 h-[34px] items-center pl-2 pr-2 py-1 relative rounded-lg shrink-0 w-[226px] cursor-pointer hover:bg-gray-50 transition-colors">
|
|
||||||
<div className="basis-0 content-stretch flex gap-2 grow items-center min-h-px min-w-px relative shrink-0 pl-2">
|
|
||||||
<p className="[white-space-collapse:collapse] basis-0 font-medium grow leading-[1.6] min-h-px min-w-px not-italic overflow-ellipsis overflow-hidden relative shrink-0 text-[14px] text-[#404040] text-left text-nowrap">
|
|
||||||
공지사항
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</button>
|
|
||||||
<button className="box-border content-stretch flex gap-2 h-[34px] items-center pl-2 pr-2 py-1 relative rounded-lg shrink-0 w-[226px] cursor-pointer hover:bg-gray-50 transition-colors">
|
|
||||||
<div className="basis-0 content-stretch flex gap-2 grow items-center min-h-px min-w-px relative shrink-0 pl-2">
|
|
||||||
<p className="[white-space-collapse:collapse] basis-0 font-medium grow leading-[1.6] min-h-px min-w-px not-italic overflow-ellipsis overflow-hidden relative shrink-0 text-[14px] text-[#404040] text-left text-nowrap">
|
|
||||||
학습 자료실
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</button>
|
|
||||||
<button className="box-border content-stretch flex gap-2 h-[34px] items-center pl-2 pr-2 py-1 relative rounded-lg shrink-0 w-[226px] cursor-pointer hover:bg-gray-50 transition-colors">
|
|
||||||
<div className="basis-0 content-stretch flex gap-2 grow items-center min-h-px min-w-px relative shrink-0 pl-2">
|
|
||||||
<p className="[white-space-collapse:collapse] basis-0 font-medium grow leading-[1.6] min-h-px min-w-px not-italic overflow-ellipsis overflow-hidden relative shrink-0 text-[14px] text-[#404040] text-left text-nowrap">
|
|
||||||
로그/접속 기록
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</button>
|
|
||||||
<button className="box-border content-stretch flex gap-2 h-[34px] items-center pl-2 pr-2 py-1 relative rounded-lg shrink-0 w-[226px] cursor-pointer hover:bg-gray-50 transition-colors">
|
|
||||||
<div className="basis-0 content-stretch flex gap-2 grow items-center min-h-px min-w-px relative shrink-0 pl-2">
|
|
||||||
<p className="[white-space-collapse:collapse] basis-0 font-medium grow leading-[1.6] min-h-px min-w-px not-italic overflow-ellipsis overflow-hidden relative shrink-0 text-[14px] text-[#404040] text-left text-nowrap">
|
|
||||||
배너 관리
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{/* 로그아웃 */}
|
|
||||||
<div className="content-stretch flex gap-[9px] items-center relative shrink-0">
|
|
||||||
<div className="flex items-center justify-center relative shrink-0">
|
|
||||||
<div className="flex-none rotate-[180deg] scale-y-[-100%]">
|
|
||||||
<div className="h-[23.12px] relative w-[22px]">
|
|
||||||
<Logout />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<button
|
|
||||||
onClick={() => {
|
|
||||||
localStorage.removeItem('isLoggedIn');
|
|
||||||
localStorage.removeItem('isAdminLoggedIn');
|
|
||||||
router.push('/');
|
|
||||||
}}
|
|
||||||
className="content-stretch flex h-[36px] items-center justify-between relative shrink-0 w-[76px] cursor-pointer hover:opacity-80 transition-opacity"
|
|
||||||
>
|
|
||||||
<div className="box-border content-stretch flex gap-[10px] items-center justify-center px-[10px] py-[5px] relative shrink-0">
|
|
||||||
<p className="font-medium leading-[1.6] not-italic relative shrink-0 text-[16px] text-[#404040] text-nowrap whitespace-pre">
|
|
||||||
로그아웃
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* 메인 콘텐츠 */}
|
|
||||||
<div className="absolute left-[250px] top-0 right-0 min-h-screen">
|
|
||||||
{/* 페이지 타이틀 탭 */}
|
|
||||||
<div className="absolute content-stretch flex gap-[24px] items-center left-[48px] top-[45px]">
|
|
||||||
<button
|
|
||||||
onClick={() => setSelectedTab('전체')}
|
|
||||||
className={`border-b-[2px] border-solid box-border content-stretch flex gap-[150px] items-center relative shrink-0 ${selectedTab === '전체' ? 'border-[#2b82e8]' : 'border-transparent'}`}
|
|
||||||
>
|
|
||||||
<div className="box-border content-stretch flex gap-[10px] items-center justify-center p-[10px] relative shrink-0">
|
|
||||||
<p className={`font-bold leading-[normal] not-italic relative shrink-0 text-[18px] text-nowrap whitespace-pre ${selectedTab === '전체' ? 'text-[#515151]' : 'text-[#515151]'}`}>
|
|
||||||
전체
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
onClick={() => setSelectedTab('학습자')}
|
|
||||||
className={`border-b-[2px] border-solid box-border content-stretch flex gap-[150px] items-center relative shrink-0 ${selectedTab === '학습자' ? 'border-[#2b82e8]' : 'border-transparent'}`}
|
|
||||||
>
|
|
||||||
<div className="box-border content-stretch flex gap-[10px] items-center justify-center p-[10px] relative shrink-0">
|
|
||||||
<p className="font-bold leading-[normal] not-italic relative shrink-0 text-[#515151] text-[18px] text-nowrap whitespace-pre">
|
|
||||||
학습자
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
onClick={() => setSelectedTab('강사')}
|
|
||||||
className={`border-b-[2px] border-solid box-border content-stretch flex gap-[150px] items-center relative shrink-0 ${selectedTab === '강사' ? 'border-[#2b82e8]' : 'border-transparent'}`}
|
|
||||||
>
|
|
||||||
<div className="box-border content-stretch flex gap-[10px] items-center justify-center p-[10px] relative shrink-0">
|
|
||||||
<p className="font-bold leading-[normal] not-italic relative shrink-0 text-[#515151] text-[18px] text-nowrap whitespace-pre">
|
|
||||||
강사
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
onClick={() => setSelectedTab('운영자')}
|
|
||||||
className={`border-b-[2px] border-solid box-border content-stretch flex gap-[150px] items-center relative shrink-0 ${selectedTab === '운영자' ? 'border-[#2b82e8]' : 'border-transparent'}`}
|
|
||||||
>
|
|
||||||
<div className="box-border content-stretch flex gap-[10px] items-center justify-center p-[10px] relative shrink-0">
|
|
||||||
<p className="font-bold leading-[normal] not-italic relative shrink-0 text-[#515151] text-[18px] text-nowrap whitespace-pre">
|
|
||||||
운영자
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* 사용자 목록 테이블 */}
|
|
||||||
<div className="absolute content-stretch flex flex-col items-start left-[48px] right-[101px] top-[135px]">
|
|
||||||
{/* 테이블 헤더 */}
|
|
||||||
<div className="bg-[rgba(235,247,255,0.5)] content-stretch flex h-[41px] items-center relative shrink-0 w-full">
|
|
||||||
<div className="content-stretch flex items-center relative shrink-0 w-full">
|
|
||||||
<div className="basis-0 border-[0.5px_0px_0.5px_0.5px] border-[#b9b9b9] border-solid box-border content-stretch flex gap-[10px] grow items-center min-h-px min-w-px p-[10px] relative shrink-0">
|
|
||||||
<div className="basis-0 flex flex-col font-bold grow justify-center leading-[0] min-h-px min-w-px not-italic relative shrink-0 text-[18px] text-[#515151]">
|
|
||||||
<p className="leading-[normal]">가입일</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="basis-0 border-[0.5px_0px_0.5px_0.5px] border-[#b9b9b9] border-solid box-border content-stretch flex gap-[10px] grow items-center min-h-px min-w-px p-[10px] relative shrink-0">
|
|
||||||
<div className="basis-0 flex flex-col font-bold grow justify-center leading-[0] min-h-px min-w-px not-italic relative shrink-0 text-[18px] text-[#515151]">
|
|
||||||
<p className="leading-[normal]">사용자명</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="basis-0 border-[0.5px_0px_0.5px_0.5px] border-[#b9b9b9] border-solid box-border content-stretch flex gap-[10px] grow items-center min-h-px min-w-px p-[10px] relative shrink-0">
|
|
||||||
<div className="basis-0 flex flex-col font-bold grow justify-center leading-[0] min-h-px min-w-px not-italic relative shrink-0 text-[18px] text-[#515151]">
|
|
||||||
<p className="leading-[normal]">아이디(이메일)</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="basis-0 border-[0.5px_0px_0.5px_0.5px] border-[#b9b9b9] border-solid box-border content-stretch flex gap-[10px] grow items-center min-h-px min-w-px p-[10px] relative shrink-0">
|
|
||||||
<div className="basis-0 flex flex-col font-bold grow justify-center leading-[0] min-h-px min-w-px not-italic relative shrink-0 text-[18px] text-[#515151]">
|
|
||||||
<p className="leading-[normal]">권한</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="basis-0 border-[0.5px_0px_0.5px_0.5px] border-[#b9b9b9] border-solid box-border content-stretch flex gap-[10px] grow items-center min-h-px min-w-px p-[10px] relative shrink-0">
|
|
||||||
<div className="basis-0 flex flex-col font-bold grow justify-center leading-[0] min-h-px min-w-px not-italic relative shrink-0 text-[18px] text-[#515151]">
|
|
||||||
<p className="leading-[normal]">계정 상태</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="basis-0 border-[0.5px_0px_0.5px_0.5px] border-[#b9b9b9] border-solid box-border content-stretch flex gap-[10px] grow items-center min-h-px min-w-px p-[10px] relative shrink-0">
|
|
||||||
<div className="basis-0 flex flex-col font-bold grow justify-center leading-[0] min-h-px min-w-px not-italic relative shrink-0 text-[18px] text-[#515151]">
|
|
||||||
<p className="leading-[normal]">계정 관리</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* 빈 상태 메시지 */}
|
|
||||||
<div className="relative w-full flex items-center justify-center" style={{ minHeight: '400px' }}>
|
|
||||||
<div className="content-stretch flex flex-col gap-[16px] items-center relative shrink-0">
|
|
||||||
<div className="content-stretch flex flex-col gap-[2px] items-center relative shrink-0">
|
|
||||||
<p className="font-medium leading-[1.6] not-italic relative shrink-0 text-[16px] text-[#969696] text-nowrap whitespace-pre">
|
|
||||||
존재하는 계정이 없어요.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 일반 사용자일 경우 기존 메인 페이지 표시
|
// 일반 사용자일 경우 기존 메인 페이지 표시
|
||||||
|
|||||||
12
package.json
12
package.json
@@ -7,14 +7,14 @@
|
|||||||
"build": "next build",
|
"build": "next build",
|
||||||
"start": "next start",
|
"start": "next start",
|
||||||
"lint": "eslint",
|
"lint": "eslint",
|
||||||
"db:generate": "prisma generate",
|
"generate": "prisma generate",
|
||||||
"db:push": "prisma db push",
|
"push": "prisma db push",
|
||||||
"db:migrate": "prisma migrate dev",
|
"migrate": "prisma migrate dev",
|
||||||
"db:studio": "prisma studio",
|
"studio": "prisma studio",
|
||||||
"db:seed": "tsx prisma/seed.ts"
|
"seed": "npx tsx prisma/seed.ts"
|
||||||
},
|
},
|
||||||
"prisma": {
|
"prisma": {
|
||||||
"seed": "tsx prisma/seed.ts"
|
"seed": "npx tsx prisma/seed.ts"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@prisma/client": "^6.19.0",
|
"@prisma/client": "^6.19.0",
|
||||||
|
|||||||
BIN
prisma/prisma/dev.db
Normal file
BIN
prisma/prisma/dev.db
Normal file
Binary file not shown.
126
prisma/seed.ts
Normal file
126
prisma/seed.ts
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
import { PrismaClient } from '../lib/generated/prisma/client';
|
||||||
|
import bcrypt from 'bcryptjs';
|
||||||
|
|
||||||
|
const prisma = new PrismaClient();
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
console.log('🌱 시드 데이터 생성 시작...');
|
||||||
|
|
||||||
|
// 기존 데이터 삭제 (순서 중요: 외래키 관계 고려)
|
||||||
|
console.log('🗑️ 기존 데이터 삭제 중...');
|
||||||
|
await prisma.userLecture.deleteMany();
|
||||||
|
await prisma.lecture.deleteMany();
|
||||||
|
await prisma.curriculum.deleteMany();
|
||||||
|
await prisma.user.deleteMany();
|
||||||
|
|
||||||
|
// 비밀번호 해시 (기본 비밀번호: password123)
|
||||||
|
const hashedPassword = await bcrypt.hash('password123', 10);
|
||||||
|
|
||||||
|
// 1. 사용자 데이터 생성
|
||||||
|
console.log('👥 사용자 데이터 생성 중...');
|
||||||
|
const users = await prisma.user.createMany({
|
||||||
|
data: [
|
||||||
|
// 운영자
|
||||||
|
{ email: 'park@example.com', name: '박민수', password: hashedPassword, role: 'ADMIN', isActive: true },
|
||||||
|
{ email: 'oh@example.com', name: '오준혁', password: hashedPassword, role: 'ADMIN', isActive: true },
|
||||||
|
{ email: 'kwon@example.com', name: '권태영', password: hashedPassword, role: 'ADMIN', isActive: true },
|
||||||
|
|
||||||
|
// 강사
|
||||||
|
{ email: 'kim@example.com', name: '김철수', password: hashedPassword, role: 'INSTRUCTOR', isActive: true },
|
||||||
|
{ email: 'jung@example.com', name: '정대현', password: hashedPassword, role: 'INSTRUCTOR', isActive: true },
|
||||||
|
{ email: 'lim@example.com', name: '임수진', password: hashedPassword, role: 'INSTRUCTOR', isActive: true },
|
||||||
|
{ email: 'shin@example.com', name: '신동욱', password: hashedPassword, role: 'INSTRUCTOR', isActive: true },
|
||||||
|
{ email: 'jeon@example.com', name: '전혜진', password: hashedPassword, role: 'INSTRUCTOR', isActive: true },
|
||||||
|
|
||||||
|
// 학습자
|
||||||
|
{ email: 'hong@example.com', name: '홍길동', password: hashedPassword, role: 'STUDENT', isActive: true },
|
||||||
|
{ email: 'lee@example.com', name: '이영희', password: hashedPassword, role: 'STUDENT', isActive: true },
|
||||||
|
{ email: 'choi@example.com', name: '최지영', password: hashedPassword, role: 'STUDENT', isActive: true },
|
||||||
|
{ email: 'kang@example.com', name: '강미영', password: hashedPassword, role: 'STUDENT', isActive: true },
|
||||||
|
{ email: 'yoon@example.com', name: '윤성호', password: hashedPassword, role: 'STUDENT', isActive: true },
|
||||||
|
{ email: 'han@example.com', name: '한지훈', password: hashedPassword, role: 'STUDENT', isActive: true },
|
||||||
|
{ email: 'song@example.com', name: '송민경', password: hashedPassword, role: 'STUDENT', isActive: true },
|
||||||
|
{ email: 'ryu@example.com', name: '류현우', password: hashedPassword, role: 'STUDENT', isActive: true },
|
||||||
|
{ email: 'cho@example.com', name: '조은서', password: hashedPassword, role: 'STUDENT', isActive: true },
|
||||||
|
{ email: 'bae@example.com', name: '배성민', password: hashedPassword, role: 'STUDENT', isActive: true },
|
||||||
|
{ email: 'namgung@example.com', name: '남궁준', password: hashedPassword, role: 'STUDENT', isActive: true },
|
||||||
|
{ email: 'seo@example.com', name: '서아름', password: hashedPassword, role: 'STUDENT', isActive: true },
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(`✅ ${users.count}명의 사용자 생성 완료`);
|
||||||
|
|
||||||
|
// 사용자 ID 조회 (강사와 등록자로 사용)
|
||||||
|
const instructorKim = await prisma.user.findUnique({ where: { email: 'kim@example.com' } });
|
||||||
|
const registrarLee = await prisma.user.findUnique({ where: { email: 'lee@example.com' } });
|
||||||
|
const registrarPark = await prisma.user.findUnique({ where: { email: 'park@example.com' } });
|
||||||
|
const registrarKim = await prisma.user.findUnique({ where: { email: 'kim@example.com' } });
|
||||||
|
|
||||||
|
if (!instructorKim || !registrarLee || !registrarPark || !registrarKim) {
|
||||||
|
throw new Error('필수 사용자를 찾을 수 없습니다.');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 교육 과정 데이터 생성
|
||||||
|
console.log('📚 교육 과정 데이터 생성 중...');
|
||||||
|
const curriculum1 = await prisma.curriculum.create({
|
||||||
|
data: {
|
||||||
|
title: '방사선 적용',
|
||||||
|
thumbnailImage: null,
|
||||||
|
instructorId: instructorKim.id,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const curriculum2 = await prisma.curriculum.create({
|
||||||
|
data: {
|
||||||
|
title: '방사선 원리',
|
||||||
|
thumbnailImage: null,
|
||||||
|
instructorId: instructorKim.id,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('✅ 교육 과정 생성 완료');
|
||||||
|
|
||||||
|
// 3. 강좌 데이터 생성
|
||||||
|
console.log('📖 강좌 데이터 생성 중...');
|
||||||
|
const lectures = await prisma.lecture.createMany({
|
||||||
|
data: [
|
||||||
|
// 방사선 적용 강좌들
|
||||||
|
{ title: '방사선 기본 원리', attachmentFile: '파일1.pdf', evaluationQuestionCount: 10, curriculumId: curriculum1.id, registrantId: registrarLee.id },
|
||||||
|
{ title: '방사선 안전 관리', attachmentFile: '파일2.pdf', evaluationQuestionCount: 15, curriculumId: curriculum1.id, registrantId: registrarLee.id },
|
||||||
|
{ title: '방사선 장비 사용법', attachmentFile: '파일4.pdf', evaluationQuestionCount: 8, curriculumId: curriculum1.id, registrantId: registrarLee.id },
|
||||||
|
{ title: '방사선 보호 장비', attachmentFile: '파일6.pdf', evaluationQuestionCount: 14, curriculumId: curriculum1.id, registrantId: registrarLee.id },
|
||||||
|
{ title: '방사선 응용 실습', attachmentFile: '파일7.pdf', evaluationQuestionCount: 16, curriculumId: curriculum1.id, registrantId: registrarLee.id },
|
||||||
|
{ title: '방사선 의료 응용', attachmentFile: '파일9.pdf', evaluationQuestionCount: 18, curriculumId: curriculum1.id, registrantId: registrarLee.id },
|
||||||
|
{ title: '방사선 산업 응용', attachmentFile: '파일10.pdf', evaluationQuestionCount: 13, curriculumId: curriculum1.id, registrantId: registrarLee.id },
|
||||||
|
{ title: '방사선 환경 모니터링', attachmentFile: '파일12.pdf', evaluationQuestionCount: 9, curriculumId: curriculum1.id, registrantId: registrarLee.id },
|
||||||
|
{ title: '방사선 검사 기법', attachmentFile: '파일13.pdf', evaluationQuestionCount: 19, curriculumId: curriculum1.id, registrantId: registrarLee.id },
|
||||||
|
{ title: '방사선 처리 기술', attachmentFile: '파일15.pdf', evaluationQuestionCount: 7, curriculumId: curriculum1.id, registrantId: registrarLee.id },
|
||||||
|
{ title: '방사선 품질 관리', attachmentFile: '파일16.pdf', evaluationQuestionCount: 22, curriculumId: curriculum1.id, registrantId: registrarLee.id },
|
||||||
|
{ title: '방사선 계측법', attachmentFile: '파일18.pdf', evaluationQuestionCount: 24, curriculumId: curriculum1.id, registrantId: registrarLee.id },
|
||||||
|
{ title: '방사선 안전 규정', attachmentFile: '파일19.pdf', evaluationQuestionCount: 5, curriculumId: curriculum1.id, registrantId: registrarLee.id },
|
||||||
|
|
||||||
|
// 방사선 원리 강좌들
|
||||||
|
{ title: '방사선 측정 기법', attachmentFile: '파일3.pdf', evaluationQuestionCount: 12, curriculumId: curriculum2.id, registrantId: registrarKim.id },
|
||||||
|
{ title: '방사선 물리학 기초', attachmentFile: '파일5.pdf', evaluationQuestionCount: 20, curriculumId: curriculum2.id, registrantId: registrarKim.id },
|
||||||
|
{ title: '방사선 화학 반응', attachmentFile: '파일8.pdf', evaluationQuestionCount: 11, curriculumId: curriculum2.id, registrantId: registrarKim.id },
|
||||||
|
{ title: '방사선 생물학', attachmentFile: '파일11.pdf', evaluationQuestionCount: 17, curriculumId: curriculum2.id, registrantId: registrarKim.id },
|
||||||
|
{ title: '방사선 에너지 전달', attachmentFile: '파일14.pdf', evaluationQuestionCount: 21, curriculumId: curriculum2.id, registrantId: registrarKim.id },
|
||||||
|
{ title: '방사선 방어 이론', attachmentFile: '파일17.pdf', evaluationQuestionCount: 6, curriculumId: curriculum2.id, registrantId: registrarKim.id },
|
||||||
|
{ title: '방사선 핵물리학', attachmentFile: '파일20.pdf', evaluationQuestionCount: 25, curriculumId: curriculum2.id, registrantId: registrarKim.id },
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(`✅ ${lectures.count}개의 강좌 생성 완료`);
|
||||||
|
|
||||||
|
console.log('🎉 시드 데이터 생성 완료!');
|
||||||
|
}
|
||||||
|
|
||||||
|
main()
|
||||||
|
.catch((e) => {
|
||||||
|
console.error('❌ 시드 데이터 생성 실패:', e);
|
||||||
|
process.exit(1);
|
||||||
|
})
|
||||||
|
.finally(async () => {
|
||||||
|
await prisma.$disconnect();
|
||||||
|
});
|
||||||
|
|
||||||
7
public/svg/checkbox_off.tsx
Normal file
7
public/svg/checkbox_off.tsx
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
export default function CheckboxOff() {
|
||||||
|
return (
|
||||||
|
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<rect x="0.5" y="0.5" width="17" height="17" rx="3.5" stroke="#B9B9B9" />
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
}
|
||||||
15
public/svg/checkbox_on.tsx
Normal file
15
public/svg/checkbox_on.tsx
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
export default function CheckboxOn() {
|
||||||
|
return (
|
||||||
|
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<rect width="18" height="18" rx="4" fill="#515151" />
|
||||||
|
<g clipPath="url(#clip0_40000058_4306)">
|
||||||
|
<path d="M7.49999 11.0849L5.41499 8.99992L4.70499 9.70492L7.49999 12.4999L13.5 6.49992L12.795 5.79492L7.49999 11.0849Z" fill="white" />
|
||||||
|
</g>
|
||||||
|
<defs>
|
||||||
|
<clipPath id="clip0_40000058_4306">
|
||||||
|
<rect width="12" height="12" fill="white" transform="translate(3 3)" />
|
||||||
|
</clipPath>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
}
|
||||||
8
public/svg/pagenav_leftoff.tsx
Normal file
8
public/svg/pagenav_leftoff.tsx
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
export default function PageNavLeftOff() {
|
||||||
|
return (
|
||||||
|
<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M11.2002 0.5H18.7998C21.8166 0.5 23.2067 0.506341 24.3174 0.867188C26.6007 1.60907 28.3909 3.39933 29.1328 5.68262C29.4937 6.79334 29.5 8.1834 29.5 11.2002V18.7998C29.5 21.8166 29.4937 23.2067 29.1328 24.3174C28.3909 26.6007 26.6007 28.3909 24.3174 29.1328C23.2067 29.4937 21.8166 29.5 18.7998 29.5H11.2002C8.1834 29.5 6.79334 29.4937 5.68262 29.1328C3.39933 28.3909 1.60907 26.6007 0.867188 24.3174C0.506341 23.2067 0.5 21.8166 0.5 18.7998V11.2002C0.5 8.1834 0.506341 6.79334 0.867188 5.68262C1.60907 3.39933 3.39933 1.60907 5.68262 0.867188C6.79334 0.506341 8.1834 0.5 11.2002 0.5Z" stroke="#EEEEEE" />
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M18.7002 21.5193C18.3199 21.9522 17.6592 21.9961 17.2246 21.6173L11.9708 17.0382C10.7328 15.9592 10.7328 14.0408 11.9708 12.9618L17.2246 8.38272C17.6592 8.00388 18.3199 8.04776 18.7002 8.48071C19.0805 8.91367 19.0365 9.57175 18.6018 9.95059L13.3481 14.5296C13.0624 14.7786 13.0624 15.2213 13.3481 15.4703L18.6018 20.0494C19.0365 20.4282 19.0805 21.0863 18.7002 21.5193Z" fill="#E1E1E1" />
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
}
|
||||||
8
public/svg/pagenav_lefton.tsx
Normal file
8
public/svg/pagenav_lefton.tsx
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
export default function PageNavLeftOn() {
|
||||||
|
return (
|
||||||
|
<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M11.2002 0.5H18.7998C21.8166 0.5 23.2067 0.506341 24.3174 0.867188C26.6007 1.60907 28.3909 3.39933 29.1328 5.68262C29.4937 6.79334 29.5 8.1834 29.5 11.2002V18.7998C29.5 21.8166 29.4937 23.2067 29.1328 24.3174C28.3909 26.6007 26.6007 28.3909 24.3174 29.1328C23.2067 29.4937 21.8166 29.5 18.7998 29.5H11.2002C8.1834 29.5 6.79334 29.4937 5.68262 29.1328C3.39933 28.3909 1.60907 26.6007 0.867188 24.3174C0.506341 23.2067 0.5 21.8166 0.5 18.7998V11.2002C0.5 8.1834 0.506341 6.79334 0.867188 5.68262C1.60907 3.39933 3.39933 1.60907 5.68262 0.867188C6.79334 0.506341 8.1834 0.5 11.2002 0.5Z" stroke="#EEEEEE" />
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M18.7002 21.5193C18.3199 21.9522 17.6592 21.9961 17.2246 21.6173L11.9708 17.0382C10.7328 15.9592 10.7328 14.0408 11.9708 12.9618L17.2246 8.38272C17.6592 8.00388 18.3199 8.04776 18.7002 8.48071C19.0805 8.91367 19.0365 9.57175 18.6018 9.95059L13.3481 14.5296C13.0624 14.7786 13.0624 15.2213 13.3481 15.4703L18.6018 20.0494C19.0365 20.4282 19.0805 21.0863 18.7002 21.5193Z" fill="#515151" />
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
}
|
||||||
8
public/svg/pagenav_rightoff.tsx
Normal file
8
public/svg/pagenav_rightoff.tsx
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
export default function PageNavRightOff() {
|
||||||
|
return (
|
||||||
|
<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M11.2002 0.5H18.7998C21.8166 0.5 23.2067 0.506341 24.3174 0.867188C26.6007 1.60907 28.3909 3.39933 29.1328 5.68262C29.4937 6.79334 29.5 8.1834 29.5 11.2002V18.7998C29.5 21.8166 29.4937 23.2067 29.1328 24.3174C28.3909 26.6007 26.6007 28.3909 24.3174 29.1328C23.2067 29.4937 21.8166 29.5 18.7998 29.5H11.2002C8.1834 29.5 6.79334 29.4937 5.68262 29.1328C3.39933 28.3909 1.60907 26.6007 0.867188 24.3174C0.506341 23.2067 0.5 21.8166 0.5 18.7998V11.2002C0.5 8.1834 0.506341 6.79334 0.867188 5.68262C1.60907 3.39933 3.39933 1.60907 5.68262 0.867188C6.79334 0.506341 8.1834 0.5 11.2002 0.5Z" stroke="#EEEEEE" />
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M11.2998 8.48074C11.6801 8.04778 12.3408 8.00391 12.7754 8.38275L18.0292 12.9618C19.2672 14.0408 19.2672 15.9592 18.0292 17.0382L12.7754 21.6173C12.3408 21.9961 11.6801 21.9522 11.2998 21.5193C10.9195 21.0863 10.9635 20.4282 11.3982 20.0494L16.6519 15.4704C16.9376 15.2214 16.9376 14.7787 16.6519 14.5297L11.3982 9.95061C10.9635 9.57178 10.9195 8.91369 11.2998 8.48074Z" fill="#E1E1E1" />
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
}
|
||||||
8
public/svg/pagenav_righton.tsx
Normal file
8
public/svg/pagenav_righton.tsx
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
export default function PageNavRightOn() {
|
||||||
|
return (
|
||||||
|
<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M11.2002 0.5H18.7998C21.8166 0.5 23.2067 0.506341 24.3174 0.867188C26.6007 1.60907 28.3909 3.39933 29.1328 5.68262C29.4937 6.79334 29.5 8.1834 29.5 11.2002V18.7998C29.5 21.8166 29.4937 23.2067 29.1328 24.3174C28.3909 26.6007 26.6007 28.3909 24.3174 29.1328C23.2067 29.4937 21.8166 29.5 18.7998 29.5H11.2002C8.1834 29.5 6.79334 29.4937 5.68262 29.1328C3.39933 28.3909 1.60907 26.6007 0.867188 24.3174C0.506341 23.2067 0.5 21.8166 0.5 18.7998V11.2002C0.5 8.1834 0.506341 6.79334 0.867188 5.68262C1.60907 3.39933 3.39933 1.60907 5.68262 0.867188C6.79334 0.506341 8.1834 0.5 11.2002 0.5Z" stroke="#EEEEEE" />
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M11.2998 8.48074C11.6801 8.04778 12.3408 8.00391 12.7754 8.38275L18.0292 12.9618C19.2672 14.0408 19.2672 15.9592 18.0292 17.0382L12.7754 21.6173C12.3408 21.9961 11.6801 21.9522 11.2998 21.5193C10.9194 21.0863 10.9635 20.4282 11.3981 20.0494L16.6519 15.4704C16.9376 15.2214 16.9376 14.7787 16.6519 14.5297L11.3982 9.95062C10.9635 9.57178 10.9195 8.91369 11.2998 8.48074Z" fill="#515151" />
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user