admin 페이지 기본 설정1
This commit is contained in:
@@ -18,6 +18,7 @@ export default function NavBar() {
|
|||||||
const userMenuRef = useRef<HTMLDivElement | null>(null);
|
const userMenuRef = useRef<HTMLDivElement | null>(null);
|
||||||
const userButtonRef = useRef<HTMLButtonElement | null>(null);
|
const userButtonRef = useRef<HTMLButtonElement | null>(null);
|
||||||
const hideCenterNav = /^\/[^/]+\/review$/.test(pathname);
|
const hideCenterNav = /^\/[^/]+\/review$/.test(pathname);
|
||||||
|
const isAdminPage = pathname.startsWith('/admin-id');
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!isUserMenuOpen) return;
|
if (!isUserMenuOpen) return;
|
||||||
@@ -51,7 +52,7 @@ export default function NavBar() {
|
|||||||
<MainLogoSvg width={46.703} height={36} />
|
<MainLogoSvg width={46.703} height={36} />
|
||||||
<span className="text-2xl font-extrabold leading-[1.45] text-white">XR LMS</span>
|
<span className="text-2xl font-extrabold leading-[1.45] text-white">XR LMS</span>
|
||||||
</Link>
|
</Link>
|
||||||
{!hideCenterNav && (
|
{!hideCenterNav && !isAdminPage && (
|
||||||
<nav className="flex h-full items-center">
|
<nav className="flex h-full items-center">
|
||||||
{NAV_ITEMS.map((item) => {
|
{NAV_ITEMS.map((item) => {
|
||||||
return (
|
return (
|
||||||
@@ -68,46 +69,59 @@ export default function NavBar() {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="relative flex items-center gap-2">
|
<div className="relative flex items-center gap-2">
|
||||||
<Link href="/menu/courses" className="px-4 py-2 text-[16px] font-semibold text-white">
|
{isAdminPage ? (
|
||||||
내 강좌실
|
<>
|
||||||
</Link>
|
<Link href="/menu/account" className="px-4 py-2 text-[16px] font-semibold text-white">
|
||||||
<button
|
내 정보
|
||||||
ref={userButtonRef}
|
</Link>
|
||||||
type="button"
|
<Link href="/login" className="px-4 py-2 text-[16px] font-semibold text-white">
|
||||||
onClick={() => setIsUserMenuOpen((v) => !v)}
|
로그아웃
|
||||||
aria-haspopup="menu"
|
</Link>
|
||||||
aria-expanded={isUserMenuOpen}
|
</>
|
||||||
className="flex items-center gap-1 px-4 py-2 text-[16px] font-semibold text-white cursor-pointer"
|
) : (
|
||||||
>
|
<>
|
||||||
김이름
|
<Link href="/menu/courses" className="px-4 py-2 text-[16px] font-semibold text-white">
|
||||||
<ChevronDownSvg
|
내 강좌실
|
||||||
width={16}
|
|
||||||
height={16}
|
|
||||||
className={["transition-transform", isUserMenuOpen ? "rotate-180" : "rotate-0"].join(" ")}
|
|
||||||
/>
|
|
||||||
</button>
|
|
||||||
{isUserMenuOpen && (
|
|
||||||
<div
|
|
||||||
ref={userMenuRef}
|
|
||||||
role="menu"
|
|
||||||
aria-label="사용자 메뉴"
|
|
||||||
className="absolute right-0 top-full mt-2 bg-white rounded-lg shadow-[0_0_8px_0_rgba(0,0,0,0.25)] p-3 z-50"
|
|
||||||
>
|
|
||||||
<Link
|
|
||||||
role="menuitem"
|
|
||||||
href="/menu/account"
|
|
||||||
className="block w-full h-10 px-2 rounded-lg text-left text-[#333C47] text-[16px] font-medium leading-normal hover:bg-[rgba(236,240,255,0.5)] focus:bg-[rgba(236,240,255,0.5)] outline-none"
|
|
||||||
onClick={() => setIsUserMenuOpen(false)}
|
|
||||||
>
|
|
||||||
내 정보 수정
|
|
||||||
</Link>
|
</Link>
|
||||||
<button
|
<button
|
||||||
role="menuitem"
|
ref={userButtonRef}
|
||||||
className="w-full h-10 px-2 rounded-lg text-left text-[#333C47] text-[16px] font-medium leading-normal hover:bg-[rgba(236,240,255,0.5)] focus:bg-[rgba(236,240,255,0.5)] outline-none"
|
type="button"
|
||||||
|
onClick={() => setIsUserMenuOpen((v) => !v)}
|
||||||
|
aria-haspopup="menu"
|
||||||
|
aria-expanded={isUserMenuOpen}
|
||||||
|
className="flex items-center gap-1 px-4 py-2 text-[16px] font-semibold text-white cursor-pointer"
|
||||||
>
|
>
|
||||||
로그아웃
|
김이름
|
||||||
|
<ChevronDownSvg
|
||||||
|
width={16}
|
||||||
|
height={16}
|
||||||
|
className={["transition-transform", isUserMenuOpen ? "rotate-180" : "rotate-0"].join(" ")}
|
||||||
|
/>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
{isUserMenuOpen && (
|
||||||
|
<div
|
||||||
|
ref={userMenuRef}
|
||||||
|
role="menu"
|
||||||
|
aria-label="사용자 메뉴"
|
||||||
|
className="absolute right-0 top-full mt-2 bg-white rounded-lg shadow-[0_0_8px_0_rgba(0,0,0,0.25)] p-3 z-50"
|
||||||
|
>
|
||||||
|
<Link
|
||||||
|
role="menuitem"
|
||||||
|
href="/menu/account"
|
||||||
|
className="block w-full h-10 px-2 rounded-lg text-left text-[#333C47] text-[16px] font-medium leading-normal hover:bg-[rgba(236,240,255,0.5)] focus:bg-[rgba(236,240,255,0.5)] outline-none"
|
||||||
|
onClick={() => setIsUserMenuOpen(false)}
|
||||||
|
>
|
||||||
|
내 정보 수정
|
||||||
|
</Link>
|
||||||
|
<button
|
||||||
|
role="menuitem"
|
||||||
|
className="w-full h-10 px-2 rounded-lg text-left text-[#333C47] text-[16px] font-medium leading-normal hover:bg-[rgba(236,240,255,0.5)] focus:bg-[rgba(236,240,255,0.5)] outline-none"
|
||||||
|
>
|
||||||
|
로그아웃
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
27
src/app/admin/certificates/page.tsx
Normal file
27
src/app/admin/certificates/page.tsx
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import AdminSidebar from "@/app/components/AdminSidebar";
|
||||||
|
|
||||||
|
export default function AdminCertificatesPage() {
|
||||||
|
return (
|
||||||
|
<div className="min-h-screen flex flex-col bg-white">
|
||||||
|
<div className="flex flex-1 min-h-0">
|
||||||
|
<AdminSidebar />
|
||||||
|
|
||||||
|
<main className="flex-1 min-w-0 bg-white">
|
||||||
|
<div className="h-full flex flex-col">
|
||||||
|
<div className="h-[100px] flex items-center px-8 border-b border-[#dee1e6]">
|
||||||
|
<h1 className="text-[24px] font-bold leading-[1.5] text-[#1b2027]">
|
||||||
|
수료증 발급/검증키 관리
|
||||||
|
</h1>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex-1 px-8 pt-8 pb-20">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
27
src/app/admin/courses/page.tsx
Normal file
27
src/app/admin/courses/page.tsx
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import AdminSidebar from "@/app/components/AdminSidebar";
|
||||||
|
|
||||||
|
export default function AdminCoursesPage() {
|
||||||
|
return (
|
||||||
|
<div className="min-h-screen flex flex-col bg-white">
|
||||||
|
<div className="flex flex-1 min-h-0">
|
||||||
|
<AdminSidebar />
|
||||||
|
|
||||||
|
<main className="flex-1 min-w-0 bg-white">
|
||||||
|
<div className="h-full flex flex-col">
|
||||||
|
<div className="h-[100px] flex items-center px-8 border-b border-[#dee1e6]">
|
||||||
|
<h1 className="text-[24px] font-bold leading-[1.5] text-[#1b2027]">
|
||||||
|
교육과정 관리
|
||||||
|
</h1>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex-1 px-8 pt-8 pb-20">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
71
src/app/admin/id/page.tsx
Normal file
71
src/app/admin/id/page.tsx
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import { useState } from "react";
|
||||||
|
import AdminSidebar from "@/app/components/AdminSidebar";
|
||||||
|
|
||||||
|
type TabType = 'all' | 'learner' | 'instructor' | 'admin';
|
||||||
|
|
||||||
|
export default function AdminIdPage() {
|
||||||
|
const [activeTab, setActiveTab] = useState<TabType>('all');
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="min-h-screen flex flex-col bg-white">
|
||||||
|
{/* 메인 레이아웃 */}
|
||||||
|
<div className="flex flex-1 min-h-0">
|
||||||
|
{/* 사이드바 */}
|
||||||
|
<AdminSidebar />
|
||||||
|
|
||||||
|
{/* 메인 콘텐츠 */}
|
||||||
|
<main className="flex-1 min-w-0 bg-white">
|
||||||
|
<div className="h-full flex flex-col">
|
||||||
|
{/* 제목 영역 */}
|
||||||
|
<div className="h-[100px] flex items-center px-8 border-b border-[#dee1e6]">
|
||||||
|
<h1 className="text-[24px] font-bold leading-[1.5] text-[#1b2027]">
|
||||||
|
권한 설정
|
||||||
|
</h1>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 탭 네비게이션 */}
|
||||||
|
<div className="px-8 pt-6">
|
||||||
|
<div className="flex items-center gap-8 border-b border-[#dee1e6]">
|
||||||
|
{[
|
||||||
|
{ id: 'all' as TabType, label: '전체' },
|
||||||
|
{ id: 'learner' as TabType, label: '학습자' },
|
||||||
|
{ id: 'instructor' as TabType, label: '강사' },
|
||||||
|
{ id: 'admin' as TabType, label: '관리자' },
|
||||||
|
].map((tab) => (
|
||||||
|
<button
|
||||||
|
key={tab.id}
|
||||||
|
type="button"
|
||||||
|
onClick={() => setActiveTab(tab.id)}
|
||||||
|
className={[
|
||||||
|
"pb-4 px-1 text-[16px] font-medium leading-[1.5] transition-colors relative",
|
||||||
|
activeTab === tab.id
|
||||||
|
? "text-[#1f2b91] font-semibold"
|
||||||
|
: "text-[#6c7682]",
|
||||||
|
].join(" ")}
|
||||||
|
>
|
||||||
|
{tab.label}
|
||||||
|
{activeTab === tab.id && (
|
||||||
|
<span className="absolute bottom-0 left-0 right-0 h-0.5 bg-[#1f2b91]" />
|
||||||
|
)}
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 콘텐츠 영역 */}
|
||||||
|
<div className="flex-1 px-8 pt-8 pb-20">
|
||||||
|
<div className="rounded-lg border border-[#dee1e6] bg-white min-h-[400px] flex items-center justify-center">
|
||||||
|
<p className="text-[16px] font-medium leading-[1.5] text-[#333c47]">
|
||||||
|
현재 관리할 수 있는 회원 계정이 없습니다.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
27
src/app/admin/lessons/page.tsx
Normal file
27
src/app/admin/lessons/page.tsx
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import AdminSidebar from "@/app/components/AdminSidebar";
|
||||||
|
|
||||||
|
export default function AdminLessonsPage() {
|
||||||
|
return (
|
||||||
|
<div className="min-h-screen flex flex-col bg-white">
|
||||||
|
<div className="flex flex-1 min-h-0">
|
||||||
|
<AdminSidebar />
|
||||||
|
|
||||||
|
<main className="flex-1 min-w-0 bg-white">
|
||||||
|
<div className="h-full flex flex-col">
|
||||||
|
<div className="h-[100px] flex items-center px-8 border-b border-[#dee1e6]">
|
||||||
|
<h1 className="text-[24px] font-bold leading-[1.5] text-[#1b2027]">
|
||||||
|
강좌 관리
|
||||||
|
</h1>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex-1 px-8 pt-8 pb-20">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
27
src/app/admin/logs/page.tsx
Normal file
27
src/app/admin/logs/page.tsx
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import AdminSidebar from "@/app/components/AdminSidebar";
|
||||||
|
|
||||||
|
export default function AdminLogsPage() {
|
||||||
|
return (
|
||||||
|
<div className="min-h-screen flex flex-col bg-white">
|
||||||
|
<div className="flex flex-1 min-h-0">
|
||||||
|
<AdminSidebar />
|
||||||
|
|
||||||
|
<main className="flex-1 min-w-0 bg-white">
|
||||||
|
<div className="h-full flex flex-col">
|
||||||
|
<div className="h-[100px] flex items-center px-8 border-b border-[#dee1e6]">
|
||||||
|
<h1 className="text-[24px] font-bold leading-[1.5] text-[#1b2027]">
|
||||||
|
로그/접속 기록
|
||||||
|
</h1>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex-1 px-8 pt-8 pb-20">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
27
src/app/admin/notices/page.tsx
Normal file
27
src/app/admin/notices/page.tsx
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import AdminSidebar from "@/app/components/AdminSidebar";
|
||||||
|
|
||||||
|
export default function AdminNoticesPage() {
|
||||||
|
return (
|
||||||
|
<div className="min-h-screen flex flex-col bg-white">
|
||||||
|
<div className="flex flex-1 min-h-0">
|
||||||
|
<AdminSidebar />
|
||||||
|
|
||||||
|
<main className="flex-1 min-w-0 bg-white">
|
||||||
|
<div className="h-full flex flex-col">
|
||||||
|
<div className="h-[100px] flex items-center px-8 border-b border-[#dee1e6]">
|
||||||
|
<h1 className="text-[24px] font-bold leading-[1.5] text-[#1b2027]">
|
||||||
|
공지사항
|
||||||
|
</h1>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex-1 px-8 pt-8 pb-20">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
27
src/app/admin/questions/page.tsx
Normal file
27
src/app/admin/questions/page.tsx
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import AdminSidebar from "@/app/components/AdminSidebar";
|
||||||
|
|
||||||
|
export default function AdminQuestionsPage() {
|
||||||
|
return (
|
||||||
|
<div className="min-h-screen flex flex-col bg-white">
|
||||||
|
<div className="flex flex-1 min-h-0">
|
||||||
|
<AdminSidebar />
|
||||||
|
|
||||||
|
<main className="flex-1 min-w-0 bg-white">
|
||||||
|
<div className="h-full flex flex-col">
|
||||||
|
<div className="h-[100px] flex items-center px-8 border-b border-[#dee1e6]">
|
||||||
|
<h1 className="text-[24px] font-bold leading-[1.5] text-[#1b2027]">
|
||||||
|
문제 은행
|
||||||
|
</h1>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex-1 px-8 pt-8 pb-20">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
27
src/app/admin/resources/page.tsx
Normal file
27
src/app/admin/resources/page.tsx
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import AdminSidebar from "@/app/components/AdminSidebar";
|
||||||
|
|
||||||
|
export default function AdminResourcesPage() {
|
||||||
|
return (
|
||||||
|
<div className="min-h-screen flex flex-col bg-white">
|
||||||
|
<div className="flex flex-1 min-h-0">
|
||||||
|
<AdminSidebar />
|
||||||
|
|
||||||
|
<main className="flex-1 min-w-0 bg-white">
|
||||||
|
<div className="h-full flex flex-col">
|
||||||
|
<div className="h-[100px] flex items-center px-8 border-b border-[#dee1e6]">
|
||||||
|
<h1 className="text-[24px] font-bold leading-[1.5] text-[#1b2027]">
|
||||||
|
학습 자료실
|
||||||
|
</h1>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex-1 px-8 pt-8 pb-20">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
51
src/app/components/AdminSidebar.tsx
Normal file
51
src/app/components/AdminSidebar.tsx
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import Link from "next/link";
|
||||||
|
import { usePathname } from "next/navigation";
|
||||||
|
|
||||||
|
type NavItem = {
|
||||||
|
label: string;
|
||||||
|
href: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
const ADMIN_SIDEBAR_ITEMS: NavItem[] = [
|
||||||
|
{ label: "권한 설정", href: "/admin/id" },
|
||||||
|
{ label: "교육과정 관리", href: "/admin/courses" },
|
||||||
|
{ label: "강좌 관리", href: "/admin/lessons" },
|
||||||
|
{ label: "문제 은행", href: "/admin/questions" },
|
||||||
|
{ label: "수료증 발급/검증키 관리", href: "/admin/certificates" },
|
||||||
|
{ label: "공지사항", href: "/admin/notices" },
|
||||||
|
{ label: "학습 자료실", href: "/admin/resources" },
|
||||||
|
{ label: "로그/접속 기록", href: "/admin/logs" },
|
||||||
|
];
|
||||||
|
|
||||||
|
export default function AdminSidebar() {
|
||||||
|
const pathname = usePathname();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<aside className="w-[320px] border-r border-[#dee1e6] bg-white flex-shrink-0">
|
||||||
|
<nav className="p-4">
|
||||||
|
<div className="flex flex-col gap-1">
|
||||||
|
{ADMIN_SIDEBAR_ITEMS.map((item) => {
|
||||||
|
const isActive = pathname === item.href || (item.href !== "#" && pathname.startsWith(item.href));
|
||||||
|
return (
|
||||||
|
<Link
|
||||||
|
key={item.label}
|
||||||
|
href={item.href}
|
||||||
|
className={[
|
||||||
|
"flex h-12 items-center px-3 rounded-lg text-[16px] leading-[1.5] transition-colors",
|
||||||
|
isActive
|
||||||
|
? "bg-[rgba(236,240,255,0.5)] font-bold text-[#1f2b91]"
|
||||||
|
: "font-medium text-[#333c47] hover:bg-[rgba(0,0,0,0.02)]",
|
||||||
|
].join(" ")}
|
||||||
|
>
|
||||||
|
{item.label}
|
||||||
|
</Link>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
</aside>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
Reference in New Issue
Block a user