Files
tonePage/tone-page-web/app/console/(with-menu)/user/components/user-info-editor.tsx
2025-05-12 12:47:27 +08:00

225 lines
9.1 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
'use client';
import * as React from "react"
import { cn } from "@/lib/utils"
import { Button } from "@/components/ui/button"
import {
Drawer,
DrawerClose,
DrawerContent,
DrawerDescription,
DrawerFooter,
DrawerHeader,
DrawerTitle,
} from "@/components/ui/drawer"
import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label"
import { useUser } from "@/hooks/admin/user/use-user";
import { User } from "@/lib/types/user";
import { Skeleton } from "@/components/ui/skeleton";
import { updateUser } from "@/lib/api/admin/user";
import { AdminApi } from "@/lib/api";
import { toast } from "sonner";
import {
Alert,
AlertDescription,
AlertTitle,
} from "@/components/ui/alert"
import { AlertCircle } from "lucide-react";
import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger } from "@/components/ui/alert-dialog";
import { Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger } from "@/components/ui/dialog";
export function UserInfoEditor({
onClose,
onUserUpdate,
onUserDelete,
userId,
}: {
onClose: () => void,
onUserUpdate: (user: User) => void,
onUserDelete: (userId: string) => void,
userId: string
}) {
const { user, isLoading, error } = useUser(userId);
const [saveLoading, setSaveLoading] = React.useState(false);
const handleSave = async (user: updateUser) => {
try {
setSaveLoading(true);
const res = await AdminApi.user.update(userId, user);
if (res) {
toast.success("保存成功");
onUserUpdate(res);
} else {
throw new Error();
}
} catch (error) {
toast.error((error as Error).message || "保存失败");
} finally {
setSaveLoading(false);
}
}
const [removeLoading, setRemoveLoading] = React.useState(false);
const handleRemove = async (userId: string) => {
try {
setRemoveLoading(true);
await AdminApi.user.remove(userId);
toast.success("删除成功");
onUserDelete(userId);
onClose();
} catch (error) {
toast.error((error as Error).message || "删除失败");
} finally {
setRemoveLoading(false);
}
}
const [setPasswordLoading, setSetPasswordLoading] = React.useState(false);
const handleSetPassword = async (userId: string, password: string) => {
try {
setSetPasswordLoading(true);
await AdminApi.user.setPassword(userId, password);
toast.success("密码修改成功");
} catch (error) {
toast.error((error as Error).message || "密码修改失败");
} finally {
setSetPasswordLoading(false);
}
}
return (
<Drawer open={!!userId} onClose={onClose} >
<DrawerContent>
<DrawerHeader className="text-left">
<DrawerTitle></DrawerTitle>
<DrawerDescription></DrawerDescription>
</DrawerHeader>
{user && <ProfileForm className="px-4"
user={user}
onSetPassword={handleSetPassword}
onRemove={handleRemove}
onSubmit={(e) => {
e.preventDefault()
const formData = new FormData(e.currentTarget);
handleSave({
username: formData.get("username")?.toString()!,
nickname: formData.get("nickname")?.toString()!,
email: formData.get("email")?.toString() || null,
phone: formData.get("phone")?.toString() || null,
})
}} />}
{isLoading &&
[...Array(5)].map((_, i) => (
<Skeleton className="h-20 mx-4 my-1" key={i} />
))
}
{
error && (
<Alert variant="destructive" className="mx-4">
<AlertCircle className="h-6 w-6" />
<AlertTitle>!</AlertTitle>
<AlertDescription>{error.message}</AlertDescription>
</Alert>
)
}
<DrawerFooter className="pt-2">
<DrawerClose asChild>
<Button variant="outline"></Button>
</DrawerClose>
</DrawerFooter>
</DrawerContent>
</Drawer>
)
}
function ProfileForm({ className, user, onSetPassword, onRemove, ...props }:
React.ComponentProps<"form"> & {
user: User,
onSetPassword: (userId: string, password: string) => Promise<void>,
onRemove: (userId: string) => Promise<void>,
}) {
const [newPassword, setNewPassword] = React.useState<string>("");
return (
<form className={cn("grid items-start gap-4", className)} {...props}>
<div className="grid gap-2">
<Label htmlFor="userId">UserId</Label>
<Input id="userId" name="userId" defaultValue={user.userId} disabled />
</div>
<div className="grid gap-2">
<Label htmlFor="username"></Label>
<Input id="username" name="username" defaultValue={user.username} />
</div>
<div className="grid gap-2">
<Label htmlFor="nickname"></Label>
<Input id="nickname" name="nickname" defaultValue={user.nickname} />
</div>
<div className="grid gap-2">
<Label htmlFor="email"></Label>
<Input id="email" name="email" defaultValue={user.email} />
</div>
<div className="grid gap-2">
<Label htmlFor="phone"></Label>
<Input id="phone" name="phone" defaultValue={user.phone} />
</div>
<div className="w-full flex gap-5">
<Dialog>
<DialogTrigger asChild>
<Button type="button" variant="secondary" className="flex-1" onClick={() => setNewPassword('')}></Button>
</DialogTrigger>
<DialogContent className="sm:max-w-[425px]" >
<DialogHeader>
<DialogTitle></DialogTitle>
<DialogDescription>
6-32
</DialogDescription>
</DialogHeader>
<div className="grid gap-4 py-4">
<div className="grid grid-cols-4 items-center gap-4">
<Label htmlFor="password" className="text-right">
</Label>
<Input
id="password"
name="password"
value={newPassword}
onChange={(e) => setNewPassword(e.target.value)}
className="col-span-3"
/>
</div>
</div>
<DialogFooter>
<DialogClose asChild>
<Button type="button" onClick={() => onSetPassword(user.userId, newPassword)}></Button>
</DialogClose>
</DialogFooter>
</DialogContent>
</Dialog>
<AlertDialog>
<AlertDialogTrigger asChild>
<Button type="button" variant="destructive" className="flex-1"></Button>
</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>?</AlertDialogTitle>
<AlertDialogDescription>
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel></AlertDialogCancel>
<AlertDialogAction onClick={() => onRemove(user.userId)}></AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
</div>
<Button type="submit"></Button>
</form>
)
}