10.3 사용자 검색/정지/권한 변경 o
This commit is contained in:
71
src/app/admin/users/page.tsx
Normal file
71
src/app/admin/users/page.tsx
Normal file
@@ -0,0 +1,71 @@
|
||||
"use client";
|
||||
import useSWR from "swr";
|
||||
import { useState } from "react";
|
||||
|
||||
const fetcher = (url: string) => fetch(url).then((r) => r.json());
|
||||
|
||||
export default function AdminUsersPage() {
|
||||
const [q, setQ] = useState("");
|
||||
const { data, mutate } = useSWR<{ users: any[] }>(`/api/admin/users?q=${encodeURIComponent(q)}`, fetcher);
|
||||
const users = data?.users ?? [];
|
||||
return (
|
||||
<div>
|
||||
<h1>사용자 관리</h1>
|
||||
<div style={{ display: "flex", gap: 8, marginBottom: 12 }}>
|
||||
<input placeholder="검색(nickname/phone/name)" value={q} onChange={(e) => setQ(e.target.value)} />
|
||||
</div>
|
||||
<table style={{ width: "100%", borderCollapse: "collapse" }}>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>닉네임</th>
|
||||
<th>이름</th>
|
||||
<th>전화</th>
|
||||
<th>상태</th>
|
||||
<th>권한</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{users.map((u) => (
|
||||
<Row key={u.userId} u={u} onChanged={mutate} />
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function Row({ u, onChanged }: { u: any; onChanged: () => void }) {
|
||||
const [status, setStatus] = useState(u.status);
|
||||
const [roles, setRoles] = useState<string[]>(u.roles ?? []);
|
||||
async function save() {
|
||||
await fetch(`/api/admin/users/${u.userId}/status`, { method: "PATCH", headers: { "content-type": "application/json" }, body: JSON.stringify({ status }) });
|
||||
await fetch(`/api/admin/users/${u.userId}/roles`, { method: "PATCH", headers: { "content-type": "application/json" }, body: JSON.stringify({ roles }) });
|
||||
onChanged();
|
||||
}
|
||||
const allRoles = ["admin", "editor", "user"] as const;
|
||||
return (
|
||||
<tr>
|
||||
<td>{u.nickname}</td>
|
||||
<td>{u.name}</td>
|
||||
<td>{u.phone}</td>
|
||||
<td>
|
||||
<select value={status} onChange={(e) => setStatus(e.target.value)}>
|
||||
<option value="active">active</option>
|
||||
<option value="suspended">suspended</option>
|
||||
<option value="withdrawn">withdrawn</option>
|
||||
</select>
|
||||
</td>
|
||||
<td>
|
||||
{allRoles.map((r) => (
|
||||
<label key={r} style={{ marginRight: 8 }}>
|
||||
<input type="checkbox" checked={roles.includes(r)} onChange={(e) => setRoles((prev) => (e.target.checked ? Array.from(new Set([...prev, r])) : prev.filter((x) => x !== r)))} /> {r}
|
||||
</label>
|
||||
))}
|
||||
</td>
|
||||
<td><button onClick={save}>저장</button></td>
|
||||
</tr>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user