From 4167fcb33299a29e0638f20088bbb00cbb525f82 Mon Sep 17 00:00:00 2001 From: koreacomp5 Date: Thu, 9 Oct 2025 18:25:06 +0900 Subject: [PATCH] =?UTF-8?q?10.2=20=EA=B2=8C=EC=8B=9C=ED=8C=90=20=EC=8A=A4?= =?UTF-8?q?=ED=82=A4=EB=A7=88/=EC=84=A4=EC=A0=95=20=EA=B4=80=EB=A6=AC=20UI?= =?UTF-8?q?=20o?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/admin/boards/page.tsx | 75 ++++++++++++++++++++++++++ src/app/api/admin/boards/[id]/route.ts | 15 ++++++ src/app/api/admin/boards/route.ts | 25 +++++++++ todolist.txt | 2 +- 4 files changed, 116 insertions(+), 1 deletion(-) create mode 100644 src/app/admin/boards/page.tsx create mode 100644 src/app/api/admin/boards/[id]/route.ts create mode 100644 src/app/api/admin/boards/route.ts diff --git a/src/app/admin/boards/page.tsx b/src/app/admin/boards/page.tsx new file mode 100644 index 0000000..caa9efa --- /dev/null +++ b/src/app/admin/boards/page.tsx @@ -0,0 +1,75 @@ +"use client"; +import useSWR from "swr"; +import { useState } from "react"; + +const fetcher = (url: string) => fetch(url).then((r) => r.json()); + +export default function AdminBoardsPage() { + const { data, mutate } = useSWR<{ boards: any[] }>("/api/admin/boards", fetcher); + const boards = data?.boards ?? []; + const [savingId, setSavingId] = useState(null); + async function save(b: any) { + setSavingId(b.id); + await fetch(`/api/admin/boards/${b.id}`, { method: "PATCH", headers: { "content-type": "application/json" }, body: JSON.stringify(b) }); + setSavingId(null); + mutate(); + } + return ( +
+

게시판 설정

+ + + + + + + + + + + + + + + + {boards.map((b) => ( + + ))} + +
이름slug읽기쓰기익명비밀댓승인정렬
+
+ ); +} + +function Row({ b, onSave, saving }: { b: any; onSave: (b: any) => void; saving: boolean }) { + const [edit, setEdit] = useState(b); + return ( + + setEdit({ ...edit, name: e.target.value })} /> + setEdit({ ...edit, slug: e.target.value })} /> + + + + + + + setEdit({ ...edit, allowAnonymousPost: e.target.checked })} /> + setEdit({ ...edit, allowSecretComment: e.target.checked })} /> + setEdit({ ...edit, requiresApproval: e.target.checked })} /> + setEdit({ ...edit, sortOrder: Number(e.target.value) })} style={{ width: 80 }} /> + + + ); +} + + diff --git a/src/app/api/admin/boards/[id]/route.ts b/src/app/api/admin/boards/[id]/route.ts new file mode 100644 index 0000000..0924bc2 --- /dev/null +++ b/src/app/api/admin/boards/[id]/route.ts @@ -0,0 +1,15 @@ +import { NextResponse } from "next/server"; +import prisma from "@/lib/prisma"; + +export async function PATCH(req: Request, context: { params: Promise<{ id: string }> }) { + const { id } = await context.params; + const body = await req.json().catch(() => ({})); + const data: any = {}; + for (const k of ["name", "slug", "description", "sortOrder", "readLevel", "writeLevel", "allowAnonymousPost", "allowSecretComment", "requiresApproval", "status"]) { + if (k in body) data[k] = body[k]; + } + const updated = await prisma.board.update({ where: { id }, data }); + return NextResponse.json({ board: updated }); +} + + diff --git a/src/app/api/admin/boards/route.ts b/src/app/api/admin/boards/route.ts new file mode 100644 index 0000000..4cb4781 --- /dev/null +++ b/src/app/api/admin/boards/route.ts @@ -0,0 +1,25 @@ +import { NextResponse } from "next/server"; +import prisma from "@/lib/prisma"; + +export async function GET() { + const boards = await prisma.board.findMany({ + orderBy: { sortOrder: "asc" }, + select: { + id: true, + name: true, + slug: true, + description: true, + sortOrder: true, + readLevel: true, + writeLevel: true, + allowAnonymousPost: true, + allowSecretComment: true, + requiresApproval: true, + type: true, + status: true, + }, + }); + return NextResponse.json({ boards }); +} + + diff --git a/todolist.txt b/todolist.txt index 5ff2587..e364e4f 100644 --- a/todolist.txt +++ b/todolist.txt @@ -70,7 +70,7 @@ [관리자(Admin)] 10.1 대시보드 핵심 지표 위젯 o -10.2 게시판 스키마/설정 관리 UI +10.2 게시판 스키마/설정 관리 UI o 10.3 사용자 검색/정지/권한 변경 10.4 공지/배너 등록 및 노출 설정 10.5 감사 이력/신고 내역/열람 로그