"use client"; import { useEffect, useRef, useState } from "react"; import { useRouter } from "next/navigation"; export default function AdminSettingsPage() { const router = useRouter(); const noticeSectionRef = useRef(null); const [value, setValue] = useState(""); const [loading, setLoading] = useState(false); const [saving, setSaving] = useState(false); const [error, setError] = useState(""); const [okMsg, setOkMsg] = useState(""); const [handles, setHandles] = useState>([]); const [loadingHandles, setLoadingHandles] = useState(false); const [pendingIds, setPendingIds] = useState>(new Set()); const [notices, setNotices] = useState>([]); const [loadingNotices, setLoadingNotices] = useState(false); const [pendingNoticeIds, setPendingNoticeIds] = useState>(new Set()); const loadHandles = async () => { setLoadingHandles(true); try { const res = await fetch('/api/admin/user_handles', { cache: 'no-store' }); const data = await res.json(); if (res.ok) setHandles(data.items ?? []); } finally { setLoadingHandles(false); } }; useEffect(() => { (async () => { setLoading(true); setError(""); try { const res = await fetch('/api/cost_per_view', { cache: 'no-store' }); const data = await res.json(); if (!res.ok) throw new Error(data?.error ?? '로드 실패'); if (typeof data.value === 'number') setValue(String(data.value)); } catch (e: any) { setError(e?.message ?? '로드 실패'); } finally { setLoading(false); } })(); }, []); useEffect(() => { // 최초 진입 시 핸들 목록 로드 loadHandles(); loadNotices(); }, []); const loadNotices = async () => { setLoadingNotices(true); try { const res = await fetch('/api/notice', { cache: 'no-store' }); const data = await res.json(); if (res.ok) setNotices(data ?? []); } finally { setLoadingNotices(false); } }; const deleteNotice = async (id: string) => { if (!confirm('이 공지글을 삭제하시겠습니까? 삭제 후 되돌릴 수 없습니다.')) return; setPendingNoticeIds(prev => new Set(prev).add(id)); try { const res = await fetch(`/api/notice?id=${id}`, { method: 'DELETE' }); if (!res.ok) { const data = await res.json().catch(() => ({})); throw new Error(data?.error ?? '삭제 실패'); } setNotices(prev => prev.filter(n => n.id !== id)); } catch (e) { alert('삭제에 실패했습니다.'); } finally { setPendingNoticeIds(prev => { const next = new Set(prev); next.delete(id); return next; }); } }; const onSave = async () => { setSaving(true); setError(""); setOkMsg(""); try { const res = await fetch('/api/cost_per_view', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ value }), }); const data = await res.json(); if (!res.ok || !data?.ok) throw new Error(data?.error ?? '저장 실패'); setOkMsg('저장되었습니다'); } catch (e: any) { setError(e?.message ?? '저장 실패'); } finally { setSaving(false); } }; const toggleApprove = async (id: string, approve: boolean) => { setPendingIds(prev => new Set(prev).add(id)); try { const res = await fetch('/api/admin/user_handles/approve', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ id, approve }), }); const data = await res.json(); if (!res.ok || !data?.ok) throw new Error(data?.error ?? '변경 실패'); setHandles(prev => prev.map(h => h.id === id ? { ...h, isApproved: approve } : h)); } catch (e) { alert('변경 실패'); } finally { setPendingIds(prev => { const next = new Set(prev); next.delete(id); return next; }); } }; return (

관리자 설정

Cost Per View
setValue(e.target.value)} disabled={loading || saving} />
{loading &&
불러오는 중...
} {error &&
{error}
} {okMsg &&
{okMsg}
}
User Handle 목록
{handles.map((h) => ( ))} {handles.length === 0 && ( )}
아이콘 핸들 이메일 승인 등록일
{h.icon ? ( icon ) : (
)}
{h.handle} {h.email}
{h.isApproved ? '승인' : '미승인'}
{h.createtime?.split('T')[0]}
{loadingHandles ? '불러오는 중...' : '데이터가 없습니다'}
NoticeBoard 목록
{notices.map((n) => ( ))} {notices.length === 0 && ( )}
제목 태그 내용 게시일 관리
{n.title} {n.tag} {n.content} {new Date(n.pubDate).toISOString().split('T')[0]}
{loadingNotices ? '불러오는 중...' : '데이터가 없습니다'}
); }