83 lines
4.0 KiB
TypeScript
83 lines
4.0 KiB
TypeScript
"use client";
|
|
|
|
import { useState } from "react";
|
|
|
|
type MenuItem = { id: string; label: string; path: string; visible: boolean; order: number };
|
|
|
|
const initialMenus: MenuItem[] = [
|
|
{ id: "1", label: "홈", path: "/", visible: true, order: 1 },
|
|
{ id: "2", label: "게시판", path: "/boards", visible: true, order: 2 },
|
|
{ id: "3", label: "쿠폰", path: "/coupons", visible: false, order: 3 },
|
|
];
|
|
|
|
export default function AdminMenusPage() {
|
|
const [menus, setMenus] = useState<MenuItem[]>(initialMenus);
|
|
const [form, setForm] = useState<{ label: string; path: string; visible: boolean }>({ label: "", path: "", visible: true });
|
|
|
|
function addMenu() {
|
|
if (!form.label.trim() || !form.path.trim()) return;
|
|
const next: MenuItem = { id: crypto.randomUUID(), label: form.label, path: form.path, visible: form.visible, order: menus.length + 1 };
|
|
setMenus((m) => [...m, next]);
|
|
setForm({ label: "", path: "", visible: true });
|
|
}
|
|
|
|
function removeMenu(id: string) {
|
|
setMenus((m) => m.filter((x) => x.id !== id));
|
|
}
|
|
|
|
function toggleVisible(id: string) {
|
|
setMenus((m) => m.map((x) => (x.id === id ? { ...x, visible: !x.visible } : x)));
|
|
}
|
|
|
|
return (
|
|
<div className="space-y-6">
|
|
<header className="flex items-center justify-between">
|
|
<h1 className="text-xl md:text-2xl font-bold text-neutral-900">메뉴 관리</h1>
|
|
</header>
|
|
|
|
{/* 추가 폼 */}
|
|
<section className="rounded-xl bg-white border border-neutral-200 overflow-hidden">
|
|
<div className="px-4 py-3 border-b border-neutral-200 text-sm font-medium">메뉴 추가</div>
|
|
<div className="p-4 grid grid-cols-1 md:grid-cols-[240px_1fr_auto] gap-3 items-center">
|
|
<input className="h-10 rounded-md border border-neutral-300 px-3 text-sm" placeholder="이름" value={form.label} onChange={(e) => setForm({ ...form, label: e.target.value })} />
|
|
<input className="h-10 rounded-md border border-neutral-300 px-3 text-sm" placeholder="경로 (/path)" value={form.path} onChange={(e) => setForm({ ...form, path: e.target.value })} />
|
|
<div className="flex items-center gap-3">
|
|
<label className="flex items-center gap-1 text-sm text-neutral-700">
|
|
<input type="checkbox" checked={form.visible} onChange={(e) => setForm({ ...form, visible: e.target.checked })} /> 표시
|
|
</label>
|
|
<button className="h-10 px-4 rounded-md bg-neutral-900 text-white text-sm hover:bg-neutral-800" onClick={addMenu}>추가</button>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
{/* 목록 */}
|
|
<section className="rounded-xl bg-white border border-neutral-200 overflow-hidden">
|
|
<div className="px-4 py-2 text-xs text-neutral-500 border-b border-neutral-200 grid grid-cols-[60px_1fr_1fr_120px_120px]">
|
|
<div>#</div>
|
|
<div>이름</div>
|
|
<div>경로</div>
|
|
<div className="text-center">표시</div>
|
|
<div className="text-right">관리</div>
|
|
</div>
|
|
<ul className="divide-y divide-neutral-100">
|
|
{menus.sort((a, b) => a.order - b.order).map((m, idx) => (
|
|
<li key={m.id} className="px-4 py-3 grid grid-cols-[60px_1fr_1fr_120px_120px] items-center">
|
|
<div className="text-sm text-neutral-500">{idx + 1}</div>
|
|
<div className="truncate text-sm">{m.label}</div>
|
|
<div className="truncate text-sm text-neutral-700">{m.path}</div>
|
|
<div className="text-center">
|
|
<button onClick={() => toggleVisible(m.id)} className={`h-7 px-3 rounded-full text-xs border ${m.visible ? "bg-neutral-900 text-white border-neutral-900" : "bg-white text-neutral-700 border-neutral-300 hover:bg-neutral-100"}`}>{m.visible ? "표시" : "숨김"}</button>
|
|
</div>
|
|
<div className="text-right">
|
|
<button onClick={() => removeMenu(m.id)} className="h-7 px-3 rounded-md border border-neutral-300 text-xs hover:bg-neutral-100">삭제</button>
|
|
</div>
|
|
</li>
|
|
))}
|
|
</ul>
|
|
</section>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
|