import { useEffect, useState, useCallback } from 'react'; const token = () => localStorage.getItem('admin_token'); const authFetch = (url, opts = {}) => fetch(url, { ...opts, headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${token()}`, ...opts.headers } }); const EMPTY = { year: '', content: '', sortOrder: 0, visible: true }; export default function AdminHistory() { const [items, setItems] = useState([]); const [modal, setModal] = useState(null); // null | 'create' | 'edit' const [form, setForm] = useState(EMPTY); const [editId, setEditId] = useState(null); const [saving, setSaving] = useState(false); const [toast, setToast] = useState(null); const [search, setSearch] = useState(''); const showToast = (msg, type = 'success') => { setToast({ msg, type }); setTimeout(() => setToast(null), 2500); }; const load = useCallback(() => { authFetch('/api/admin/history').then(r => r.json()).then(setItems).catch(() => {}); }, []); useEffect(() => { load(); }, [load]); const openCreate = () => { setForm(EMPTY); setEditId(null); setModal('create'); }; const openEdit = h => { setForm({ year: h.year, content: h.content, sortOrder: h.sortOrder, visible: h.visible }); setEditId(h.id); setModal('edit'); }; const closeModal = () => { setModal(null); setForm(EMPTY); setEditId(null); }; const save = async () => { if (!form.year.trim() || !form.content.trim()) { showToast('연도와 내용은 필수입니다.', 'error'); return; } setSaving(true); try { const url = modal === 'edit' ? `/api/admin/history/${editId}` : '/api/admin/history'; const meth = modal === 'edit' ? 'PUT' : 'POST'; const r = await authFetch(url, { method: meth, body: JSON.stringify(form) }); if (!r.ok) throw new Error('저장 실패'); showToast(modal === 'edit' ? '수정되었습니다.' : '등록되었습니다.'); closeModal(); load(); } catch { showToast('저장 중 오류가 발생했습니다.', 'error'); } finally { setSaving(false); } }; const del = async id => { if (!confirm('이 항목을 삭제하시겠습니까?')) return; await authFetch(`/api/admin/history/${id}`, { method: 'DELETE' }); showToast('삭제되었습니다.'); load(); }; const toggleVisible = async h => { await authFetch(`/api/admin/history/${h.id}`, { method: 'PUT', body: JSON.stringify({ ...h, visible: !h.visible }), }); load(); }; // 연도별 그룹핑 const grouped = {}; items.filter(h => !search || h.year.includes(search) || h.content.includes(search)) .forEach(h => { (grouped[h.year] = grouped[h.year] || []).push(h); }); return (
{toast && (
{toast.msg}
)}

연혁 관리

setSearch(e.target.value)} placeholder="연도 또는 내용 검색..." style={{ padding:'6px 12px', border:'1px solid #cbd5e1', borderRadius:6, fontSize:13, flex:1, maxWidth:280 }} />
{items.length}개 항목
{/* 연도별 타임라인 테이블 */} {Object.entries(grouped).map(([year, rows]) => (
{year} {rows.length}개 항목
{['순서','내용','노출','수정','삭제'].map(h=>( ))} {rows.sort((a,b)=>a.sortOrder-b.sortOrder).map(h=>( ))}
{h}
{h.sortOrder} {!h.visible && 숨김} {h.content}
))} {Object.keys(grouped).length === 0 && (
{search ? '검색 결과 없음' : '등록된 연혁이 없습니다.'}
)} {/* 모달 */} {modal && (

{modal === 'create' ? '연혁 추가' : '연혁 수정'}