Loading packages/admin/src/pages/Accounts/AccountDetail/page.tsx +55 −0 Original line number Diff line number Diff line Loading @@ -5,6 +5,7 @@ import { Button, Input, ProgressCircle, Switch, TextArea, } from "@heroui/react"; import { ArrowLeft, Ban, MessageCircle, ShieldQuestion } from "lucide-react"; Loading Loading @@ -49,6 +50,10 @@ export const AccountDetailPage = () => { const [userIPs, setUserIPs] = useState<IPAddress[]>([]); const [userBan, setUserBan] = useState<BanInfo | null>(null); const [roleSaving, setRoleSaving] = useState(false); const [isAdmin, setAdmin] = useState(false); const [isMod, setMod] = useState(false); // Ban form const [banExpiresAt, setBanExpiresAt] = useState(""); const [banPublicNote, setBanPublicNote] = useState(""); Loading @@ -71,6 +76,8 @@ export const AccountDetailPage = () => { ); if (userResult.status === 200 && userResult.data.success) { setUser(userResult.data.user); setAdmin(userResult.data.user.isAdmin); setMod(userResult.data.user.isModerator); } else { handleError(userResult.status, userResult.data); } Loading Loading @@ -98,6 +105,27 @@ export const AccountDetailPage = () => { fetchData(); }, [sub]); const saveRoles = async () => { if (!sub) return; setRoleSaving(true); const result = await api( `/api/admin/users/user/${encodeURIComponent(sub)}/role`, "PUT", { admin: isAdmin, moderator: isMod, }, ); if (result.status === 200 && result.data.success) { toast.success(`Updated roles for ${user?.username ?? sub}`); setAdmin(Boolean((result.data.user as any).isAdmin)); setMod(Boolean((result.data.user as any).isMod)); } else { handleError(result.status, result.data); } setRoleSaving(false); }; const saveBan = async () => { if (!sub) return; setBanSaving(true); Loading Loading @@ -218,6 +246,33 @@ export const AccountDetailPage = () => { </div> </div> <div> <h4 className="text-lg font-semibold mb-2 flex items-center gap-2"> Roles </h4> <div> <Switch isSelected={isAdmin} onChange={(e) => setAdmin(e)}> <Switch.Content> <Switch.Control> <Switch.Thumb /> </Switch.Control> Admin </Switch.Content> </Switch> <Switch isSelected={isMod} onChange={(e) => setMod(e)}> <Switch.Content> <Switch.Control> <Switch.Thumb /> </Switch.Control> Moderator </Switch.Content> </Switch> <Button size="sm" onPress={saveRoles} isPending={roleSaving}> Save roles </Button> </div> </div> {/* Ban Section */} <div> <h4 className="text-lg font-semibold mb-2 flex items-center gap-2"> Loading Loading
packages/admin/src/pages/Accounts/AccountDetail/page.tsx +55 −0 Original line number Diff line number Diff line Loading @@ -5,6 +5,7 @@ import { Button, Input, ProgressCircle, Switch, TextArea, } from "@heroui/react"; import { ArrowLeft, Ban, MessageCircle, ShieldQuestion } from "lucide-react"; Loading Loading @@ -49,6 +50,10 @@ export const AccountDetailPage = () => { const [userIPs, setUserIPs] = useState<IPAddress[]>([]); const [userBan, setUserBan] = useState<BanInfo | null>(null); const [roleSaving, setRoleSaving] = useState(false); const [isAdmin, setAdmin] = useState(false); const [isMod, setMod] = useState(false); // Ban form const [banExpiresAt, setBanExpiresAt] = useState(""); const [banPublicNote, setBanPublicNote] = useState(""); Loading @@ -71,6 +76,8 @@ export const AccountDetailPage = () => { ); if (userResult.status === 200 && userResult.data.success) { setUser(userResult.data.user); setAdmin(userResult.data.user.isAdmin); setMod(userResult.data.user.isModerator); } else { handleError(userResult.status, userResult.data); } Loading Loading @@ -98,6 +105,27 @@ export const AccountDetailPage = () => { fetchData(); }, [sub]); const saveRoles = async () => { if (!sub) return; setRoleSaving(true); const result = await api( `/api/admin/users/user/${encodeURIComponent(sub)}/role`, "PUT", { admin: isAdmin, moderator: isMod, }, ); if (result.status === 200 && result.data.success) { toast.success(`Updated roles for ${user?.username ?? sub}`); setAdmin(Boolean((result.data.user as any).isAdmin)); setMod(Boolean((result.data.user as any).isMod)); } else { handleError(result.status, result.data); } setRoleSaving(false); }; const saveBan = async () => { if (!sub) return; setBanSaving(true); Loading Loading @@ -218,6 +246,33 @@ export const AccountDetailPage = () => { </div> </div> <div> <h4 className="text-lg font-semibold mb-2 flex items-center gap-2"> Roles </h4> <div> <Switch isSelected={isAdmin} onChange={(e) => setAdmin(e)}> <Switch.Content> <Switch.Control> <Switch.Thumb /> </Switch.Control> Admin </Switch.Content> </Switch> <Switch isSelected={isMod} onChange={(e) => setMod(e)}> <Switch.Content> <Switch.Control> <Switch.Thumb /> </Switch.Control> Moderator </Switch.Content> </Switch> <Button size="sm" onPress={saveRoles} isPending={roleSaving}> Save roles </Button> </div> </div> {/* Ban Section */} <div> <h4 className="text-lg font-semibold mb-2 flex items-center gap-2"> Loading