mirror of
https://github.com/Rain-kl/OpenFlare.git
synced 2026-10-10 17:26:38 +08:00
prettier
This commit is contained in:
@@ -1,14 +1,20 @@
|
||||
"use client"
|
||||
'use client';
|
||||
|
||||
import {useEffect, useState} from "react"
|
||||
import {Button} from "@/components/ui/button"
|
||||
import {Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle} from "@/components/ui/dialog"
|
||||
import {Input} from "@/components/ui/input"
|
||||
import {Label} from "@/components/ui/label"
|
||||
import {Switch} from "@/components/ui/switch"
|
||||
import {useAdminUsers} from "@/contexts/admin-users-context"
|
||||
import {useAuth} from "@/components/providers/auth-provider"
|
||||
import type {AdminUser} from "@/lib/services/admin"
|
||||
import { useEffect, useState } from 'react';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
} from '@/components/ui/dialog';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { Label } from '@/components/ui/label';
|
||||
import { Switch } from '@/components/ui/switch';
|
||||
import { useAdminUsers } from '@/contexts/admin-users-context';
|
||||
import { useAuth } from '@/components/providers/auth-provider';
|
||||
import type { AdminUser } from '@/lib/services/admin';
|
||||
import {
|
||||
AlertDialog,
|
||||
AlertDialogAction,
|
||||
@@ -17,15 +23,15 @@ import {
|
||||
AlertDialogDescription,
|
||||
AlertDialogFooter,
|
||||
AlertDialogHeader,
|
||||
AlertDialogTitle
|
||||
} from "@/components/ui/alert-dialog"
|
||||
import {Loader2} from "lucide-react"
|
||||
AlertDialogTitle,
|
||||
} from '@/components/ui/alert-dialog';
|
||||
import { Loader2 } from 'lucide-react';
|
||||
|
||||
interface EditUserForm {
|
||||
nickname: string
|
||||
email: string
|
||||
is_admin: boolean
|
||||
password?: string
|
||||
nickname: string;
|
||||
email: string;
|
||||
is_admin: boolean;
|
||||
password?: string;
|
||||
}
|
||||
|
||||
export function EditUserModal({
|
||||
@@ -33,115 +39,116 @@ export function EditUserModal({
|
||||
onClose,
|
||||
user,
|
||||
}: {
|
||||
isOpen: boolean
|
||||
onClose: () => void
|
||||
user: AdminUser | null
|
||||
isOpen: boolean;
|
||||
onClose: () => void;
|
||||
user: AdminUser | null;
|
||||
}) {
|
||||
const { updateUser, deleteUser, getUserDetail } = useAdminUsers()
|
||||
const { user: currentUser } = useAuth()
|
||||
|
||||
const isSelf = currentUser && user && currentUser.id.toString() === user.id.toString()
|
||||
const { updateUser, deleteUser, getUserDetail } = useAdminUsers();
|
||||
const { user: currentUser } = useAuth();
|
||||
|
||||
const isSelf =
|
||||
currentUser && user && currentUser.id.toString() === user.id.toString();
|
||||
|
||||
const [form, setForm] = useState<EditUserForm>({
|
||||
nickname: "",
|
||||
email: "",
|
||||
nickname: '',
|
||||
email: '',
|
||||
is_admin: false,
|
||||
password: "",
|
||||
})
|
||||
const [saving, setSaving] = useState(false)
|
||||
const [errors, setErrors] = useState<Record<string, string>>({})
|
||||
const [showDeleteConfirm, setShowDeleteConfirm] = useState(false)
|
||||
password: '',
|
||||
});
|
||||
const [saving, setSaving] = useState(false);
|
||||
const [errors, setErrors] = useState<Record<string, string>>({});
|
||||
const [showDeleteConfirm, setShowDeleteConfirm] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
let active = true
|
||||
let active = true;
|
||||
if (isOpen && user) {
|
||||
setForm({
|
||||
nickname: user.nickname || "",
|
||||
email: user.email || "",
|
||||
nickname: user.nickname || '',
|
||||
email: user.email || '',
|
||||
is_admin: user.is_admin || false,
|
||||
password: "",
|
||||
})
|
||||
setErrors({})
|
||||
password: '',
|
||||
});
|
||||
setErrors({});
|
||||
|
||||
getUserDetail(user.id)
|
||||
.then((detail) => {
|
||||
if (active && detail) {
|
||||
setForm({
|
||||
nickname: detail.nickname || "",
|
||||
email: detail.email || "",
|
||||
nickname: detail.nickname || '',
|
||||
email: detail.email || '',
|
||||
is_admin: detail.is_admin || false,
|
||||
password: "",
|
||||
})
|
||||
password: '',
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
// ignore fetching error
|
||||
})
|
||||
});
|
||||
} else {
|
||||
setSaving(false)
|
||||
setSaving(false);
|
||||
}
|
||||
return () => {
|
||||
active = false
|
||||
}
|
||||
}, [isOpen, user, getUserDetail])
|
||||
active = false;
|
||||
};
|
||||
}, [isOpen, user, getUserDetail]);
|
||||
|
||||
const validate = () => {
|
||||
const newErrors: Record<string, string> = {}
|
||||
const newErrors: Record<string, string> = {};
|
||||
|
||||
if (!form.email.trim()) {
|
||||
newErrors.email = "邮箱不能为空"
|
||||
newErrors.email = '邮箱不能为空';
|
||||
} else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(form.email.trim())) {
|
||||
newErrors.email = "邮箱格式不正确"
|
||||
newErrors.email = '邮箱格式不正确';
|
||||
}
|
||||
|
||||
if (form.password && form.password.length < 8) {
|
||||
newErrors.password = "密码长度不能少于 8 位"
|
||||
newErrors.password = '密码长度不能少于 8 位';
|
||||
}
|
||||
|
||||
setErrors(newErrors)
|
||||
return Object.keys(newErrors).length === 0
|
||||
}
|
||||
setErrors(newErrors);
|
||||
return Object.keys(newErrors).length === 0;
|
||||
};
|
||||
|
||||
const handleSave = async (e: React.FormEvent) => {
|
||||
e.preventDefault()
|
||||
if (!user || !validate()) return
|
||||
e.preventDefault();
|
||||
if (!user || !validate()) return;
|
||||
|
||||
setSaving(true)
|
||||
setSaving(true);
|
||||
try {
|
||||
await updateUser(user.id, {
|
||||
nickname: form.nickname.trim() || undefined,
|
||||
email: form.email.trim(),
|
||||
is_admin: form.is_admin,
|
||||
password: form.password?.trim() || undefined,
|
||||
})
|
||||
onClose()
|
||||
});
|
||||
onClose();
|
||||
} catch {
|
||||
// Errors are handled by context toast notifications
|
||||
} finally {
|
||||
setSaving(false)
|
||||
setSaving(false);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const handleDelete = async () => {
|
||||
if (!user) return
|
||||
setSaving(true)
|
||||
if (!user) return;
|
||||
setSaving(true);
|
||||
try {
|
||||
await deleteUser(user)
|
||||
onClose()
|
||||
await deleteUser(user);
|
||||
onClose();
|
||||
} catch {
|
||||
// Errors are handled by context toast notifications
|
||||
} finally {
|
||||
setSaving(false)
|
||||
setShowDeleteConfirm(false)
|
||||
setSaving(false);
|
||||
setShowDeleteConfirm(false);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (!user) return null
|
||||
if (!user) return null;
|
||||
|
||||
return (
|
||||
<>
|
||||
<Dialog open={isOpen} onOpenChange={(open) => !open && onClose()}>
|
||||
<DialogContent className="max-w-md">
|
||||
<DialogContent className='max-w-md'>
|
||||
<DialogHeader>
|
||||
<DialogTitle>编辑用户</DialogTitle>
|
||||
<DialogDescription>
|
||||
@@ -149,89 +156,102 @@ export function EditUserModal({
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
|
||||
<form onSubmit={handleSave} className="space-y-4 pt-2">
|
||||
<div className="space-y-1.5">
|
||||
<Label htmlFor="username" className="text-xs text-muted-foreground">用户名 (不可修改)</Label>
|
||||
<form onSubmit={handleSave} className='space-y-4 pt-2'>
|
||||
<div className='space-y-1.5'>
|
||||
<Label
|
||||
htmlFor='username'
|
||||
className='text-xs text-muted-foreground'
|
||||
>
|
||||
用户名 (不可修改)
|
||||
</Label>
|
||||
<Input
|
||||
id="username"
|
||||
id='username'
|
||||
value={user.username}
|
||||
disabled
|
||||
className="bg-muted/50 cursor-not-allowed select-none font-mono"
|
||||
className='bg-muted/50 cursor-not-allowed select-none font-mono'
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="space-y-1.5">
|
||||
<Label htmlFor="nickname">昵称 (选填)</Label>
|
||||
<div className='space-y-1.5'>
|
||||
<Label htmlFor='nickname'>昵称 (选填)</Label>
|
||||
<Input
|
||||
id="nickname"
|
||||
id='nickname'
|
||||
value={form.nickname}
|
||||
onChange={(e) => setForm((prev) => ({ ...prev, nickname: e.target.value }))}
|
||||
placeholder="请输入昵称"
|
||||
onChange={(e) =>
|
||||
setForm((prev) => ({ ...prev, nickname: e.target.value }))
|
||||
}
|
||||
placeholder='请输入昵称'
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="space-y-1.5">
|
||||
<Label htmlFor="email">邮箱</Label>
|
||||
<div className='space-y-1.5'>
|
||||
<Label htmlFor='email'>邮箱</Label>
|
||||
<Input
|
||||
id="email"
|
||||
type="email"
|
||||
id='email'
|
||||
type='email'
|
||||
value={form.email}
|
||||
onChange={(e) => setForm((prev) => ({ ...prev, email: e.target.value }))}
|
||||
placeholder="请输入邮箱地址"
|
||||
onChange={(e) =>
|
||||
setForm((prev) => ({ ...prev, email: e.target.value }))
|
||||
}
|
||||
placeholder='请输入邮箱地址'
|
||||
/>
|
||||
{errors.email && (
|
||||
<p className="text-xs text-destructive">{errors.email}</p>
|
||||
<p className='text-xs text-destructive'>{errors.email}</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="space-y-1.5">
|
||||
<Label htmlFor="password">重置密码 (选填)</Label>
|
||||
<div className='space-y-1.5'>
|
||||
<Label htmlFor='password'>重置密码 (选填)</Label>
|
||||
<Input
|
||||
id="password"
|
||||
type="password"
|
||||
id='password'
|
||||
type='password'
|
||||
value={form.password}
|
||||
onChange={(e) => setForm((prev) => ({ ...prev, password: e.target.value }))}
|
||||
placeholder="留空表示保持当前密码,输入则重置 (至少 8 位)"
|
||||
onChange={(e) =>
|
||||
setForm((prev) => ({ ...prev, password: e.target.value }))
|
||||
}
|
||||
placeholder='留空表示保持当前密码,输入则重置 (至少 8 位)'
|
||||
/>
|
||||
{errors.password && (
|
||||
<p className="text-xs text-destructive">{errors.password}</p>
|
||||
<p className='text-xs text-destructive'>{errors.password}</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="flex items-center justify-between rounded-lg border border-dashed p-3 bg-muted/10">
|
||||
<div className='flex items-center justify-between rounded-lg border border-dashed p-3 bg-muted/10'>
|
||||
<div>
|
||||
<div className="font-medium text-sm">管理员权限</div>
|
||||
<div className="text-xs text-muted-foreground">
|
||||
{isSelf ? "不能撤销当前登录用户的管理员权限。" : "开启后此账号将拥有后台管理权限。"}
|
||||
<div className='font-medium text-sm'>管理员权限</div>
|
||||
<div className='text-xs text-muted-foreground'>
|
||||
{isSelf
|
||||
? '不能撤销当前登录用户的管理员权限。'
|
||||
: '开启后此账号将拥有后台管理权限。'}
|
||||
</div>
|
||||
</div>
|
||||
<Switch
|
||||
checked={form.is_admin}
|
||||
disabled={!!isSelf || saving}
|
||||
onCheckedChange={async (checked) => {
|
||||
setForm((prev) => ({ ...prev, is_admin: checked }))
|
||||
setSaving(true)
|
||||
setForm((prev) => ({ ...prev, is_admin: checked }));
|
||||
setSaving(true);
|
||||
try {
|
||||
await updateUser(user.id, {
|
||||
nickname: form.nickname.trim() || undefined,
|
||||
email: form.email.trim(),
|
||||
is_admin: checked,
|
||||
})
|
||||
});
|
||||
} catch {
|
||||
setForm((prev) => ({ ...prev, is_admin: !checked }))
|
||||
setForm((prev) => ({ ...prev, is_admin: !checked }));
|
||||
} finally {
|
||||
setSaving(false)
|
||||
setSaving(false);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex justify-between items-center pt-2 border-t mt-2">
|
||||
<div className='flex justify-between items-center pt-2 border-t mt-2'>
|
||||
<div>
|
||||
{!user.is_admin && (
|
||||
<Button
|
||||
type="button"
|
||||
variant="destructive"
|
||||
type='button'
|
||||
variant='destructive'
|
||||
onClick={() => setShowDeleteConfirm(true)}
|
||||
disabled={saving}
|
||||
>
|
||||
@@ -239,12 +259,17 @@ export function EditUserModal({
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex gap-2">
|
||||
<Button variant="outline" type="button" onClick={onClose} disabled={saving}>
|
||||
<div className='flex gap-2'>
|
||||
<Button
|
||||
variant='outline'
|
||||
type='button'
|
||||
onClick={onClose}
|
||||
disabled={saving}
|
||||
>
|
||||
取消
|
||||
</Button>
|
||||
<Button type="submit" disabled={saving} variant="secondary">
|
||||
{saving ? "保存中..." : "保存"}
|
||||
<Button type='submit' disabled={saving} variant='secondary'>
|
||||
{saving ? '保存中...' : '保存'}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -257,24 +282,25 @@ export function EditUserModal({
|
||||
<AlertDialogHeader>
|
||||
<AlertDialogTitle>确认删除用户</AlertDialogTitle>
|
||||
<AlertDialogDescription>
|
||||
确定要删除用户 {user.nickname || user.username} 吗?该操作会移除用户账号,删除后无法撤销。
|
||||
确定要删除用户 {user.nickname || user.username}{' '}
|
||||
吗?该操作会移除用户账号,删除后无法撤销。
|
||||
</AlertDialogDescription>
|
||||
</AlertDialogHeader>
|
||||
<AlertDialogFooter>
|
||||
<AlertDialogCancel disabled={saving}>取消</AlertDialogCancel>
|
||||
<AlertDialogAction
|
||||
onClick={(e) => {
|
||||
e.preventDefault()
|
||||
handleDelete()
|
||||
e.preventDefault();
|
||||
handleDelete();
|
||||
}}
|
||||
disabled={saving}
|
||||
>
|
||||
{saving && <Loader2 className="size-3 animate-spin mr-1" />}
|
||||
{saving && <Loader2 className='size-3 animate-spin mr-1' />}
|
||||
确认删除
|
||||
</AlertDialogAction>
|
||||
</AlertDialogFooter>
|
||||
</AlertDialogContent>
|
||||
</AlertDialog>
|
||||
</>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user