feat
This commit is contained in:
@@ -12,7 +12,6 @@ import {
|
||||
MessagesSquareIcon,
|
||||
LogOutIcon,
|
||||
PlusIcon,
|
||||
UserPlusIcon,
|
||||
SwordsIcon,
|
||||
} from "lucide-react";
|
||||
|
||||
@@ -65,15 +64,6 @@ export function AdminHeader() {
|
||||
<BookOpenIcon data-icon="inline-start" />
|
||||
<span className="hidden sm:inline">Glossary</span>
|
||||
</Button>
|
||||
<Button
|
||||
variant={pathname === "/admin/register" ? "secondary" : "ghost"}
|
||||
size="sm"
|
||||
nativeButton={false}
|
||||
render={<Link href="/admin/register" />}
|
||||
>
|
||||
<UserPlusIcon data-icon="inline-start" />
|
||||
<span className="hidden sm:inline">Accounts</span>
|
||||
</Button>
|
||||
<Button
|
||||
variant={pathname === "/admin" ? "secondary" : "ghost"}
|
||||
size="sm"
|
||||
|
||||
@@ -1,200 +0,0 @@
|
||||
"use client";
|
||||
|
||||
import { CircleAlertIcon, Trash2Icon, UserPlusIcon } from "lucide-react";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useActionState, useEffect, useRef, useTransition } from "react";
|
||||
import { toast } from "sonner";
|
||||
|
||||
import {
|
||||
createAccount,
|
||||
removeAccount,
|
||||
type CreateAccountState,
|
||||
} from "@/app/admin/register/actions";
|
||||
import { DeleteConfirmationDialog } from "@/components/admin/delete-confirmation-dialog";
|
||||
import {
|
||||
Alert,
|
||||
AlertDescription,
|
||||
AlertTitle,
|
||||
} from "@/components/ui/alert";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
CardDescription,
|
||||
CardFooter,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} from "@/components/ui/card";
|
||||
import {
|
||||
Field,
|
||||
FieldDescription,
|
||||
FieldGroup,
|
||||
FieldLabel,
|
||||
} from "@/components/ui/field";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Spinner } from "@/components/ui/spinner";
|
||||
|
||||
const initialCreateAccountState: CreateAccountState = {
|
||||
status: "idle",
|
||||
message: "",
|
||||
};
|
||||
|
||||
export function RegisterCard({
|
||||
accounts,
|
||||
currentUserId,
|
||||
}: {
|
||||
accounts: Array<{ id: string; name: string; email: string; createdAt: Date }>;
|
||||
currentUserId: string;
|
||||
}) {
|
||||
const router = useRouter();
|
||||
const formRef = useRef<HTMLFormElement>(null);
|
||||
const [state, formAction, pending] = useActionState(
|
||||
createAccount,
|
||||
initialCreateAccountState,
|
||||
);
|
||||
const [removing, startRemoval] = useTransition();
|
||||
|
||||
useEffect(() => {
|
||||
if (state.status !== "success") return;
|
||||
formRef.current?.reset();
|
||||
router.refresh();
|
||||
}, [router, state]);
|
||||
|
||||
function remove(userId: string) {
|
||||
startRemoval(async () => {
|
||||
const result = await removeAccount(userId);
|
||||
if (result.status === "error") toast.error(result.message);
|
||||
else {
|
||||
toast.success("ลบบัญชีแล้ว");
|
||||
router.refresh();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="grid gap-6 lg:grid-cols-[minmax(0,28rem)_minmax(0,1fr)]">
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>สร้างบัญชี</CardTitle>
|
||||
<CardDescription>
|
||||
เพิ่มบัญชีผู้ดูแลที่สามารถเข้าสู่ระบบและจัดการเนื้อหาได้
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<form ref={formRef} action={formAction}>
|
||||
<FieldGroup>
|
||||
<Field>
|
||||
<FieldLabel htmlFor="register-name">ชื่อ</FieldLabel>
|
||||
<Input
|
||||
id="register-name"
|
||||
name="name"
|
||||
autoComplete="name"
|
||||
disabled={pending}
|
||||
required
|
||||
/>
|
||||
</Field>
|
||||
<Field>
|
||||
<FieldLabel htmlFor="register-email">อีเมล</FieldLabel>
|
||||
<Input
|
||||
id="register-email"
|
||||
name="email"
|
||||
type="email"
|
||||
autoComplete="username"
|
||||
autoCapitalize="none"
|
||||
spellCheck={false}
|
||||
disabled={pending}
|
||||
required
|
||||
/>
|
||||
</Field>
|
||||
<Field>
|
||||
<FieldLabel htmlFor="register-password">รหัสผ่าน</FieldLabel>
|
||||
<Input
|
||||
id="register-password"
|
||||
name="password"
|
||||
type="password"
|
||||
autoComplete="new-password"
|
||||
minLength={12}
|
||||
maxLength={128}
|
||||
disabled={pending}
|
||||
required
|
||||
/>
|
||||
<FieldDescription>อย่างน้อย 12 ตัวอักษร</FieldDescription>
|
||||
</Field>
|
||||
<Field>
|
||||
<FieldLabel htmlFor="register-password-confirmation">
|
||||
ยืนยันรหัสผ่าน
|
||||
</FieldLabel>
|
||||
<Input
|
||||
id="register-password-confirmation"
|
||||
name="passwordConfirmation"
|
||||
type="password"
|
||||
autoComplete="new-password"
|
||||
minLength={12}
|
||||
maxLength={128}
|
||||
disabled={pending}
|
||||
required
|
||||
/>
|
||||
</Field>
|
||||
{state.message ? (
|
||||
<Alert variant={state.status === "error" ? "destructive" : "default"}>
|
||||
<CircleAlertIcon aria-hidden="true" />
|
||||
<AlertTitle>
|
||||
{state.status === "error" ? "สร้างบัญชีไม่สำเร็จ" : "สร้างบัญชีแล้ว"}
|
||||
</AlertTitle>
|
||||
<AlertDescription>{state.message}</AlertDescription>
|
||||
</Alert>
|
||||
) : null}
|
||||
<Button className="w-full" size="lg" type="submit" disabled={pending}>
|
||||
{pending ? (
|
||||
<Spinner data-icon="inline-start" />
|
||||
) : (
|
||||
<UserPlusIcon data-icon="inline-start" aria-hidden="true" />
|
||||
)}
|
||||
{pending ? "กำลังสร้างบัญชี…" : "สร้างบัญชี"}
|
||||
</Button>
|
||||
</FieldGroup>
|
||||
</form>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>บัญชีในระบบ</CardTitle>
|
||||
<CardDescription>{accounts.length} บัญชี</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="flex flex-col gap-3">
|
||||
{accounts.map((account) => {
|
||||
const isCurrent = account.id === currentUserId;
|
||||
return (
|
||||
<div key={account.id} className="flex items-center gap-3 rounded-lg border p-3">
|
||||
<div className="min-w-0 flex-1">
|
||||
<p className="truncate font-medium">{account.name}</p>
|
||||
<p className="truncate text-sm text-muted-foreground">{account.email}</p>
|
||||
</div>
|
||||
{isCurrent ? (
|
||||
<span className="text-sm text-muted-foreground">บัญชีปัจจุบัน</span>
|
||||
) : (
|
||||
<DeleteConfirmationDialog
|
||||
trigger={<Button variant="ghost" size="icon-sm" disabled={removing} />}
|
||||
title={`ลบบัญชี ${account.name}?`}
|
||||
description="ผู้ใช้นี้จะออกจากระบบและไม่สามารถเข้าสู่ระบบได้อีก การดำเนินการนี้ย้อนกลับไม่ได้"
|
||||
confirmLabel="ลบบัญชี"
|
||||
onConfirm={() => remove(account.id)}
|
||||
>
|
||||
<Trash2Icon />
|
||||
<span className="sr-only">ลบบัญชี {account.name}</span>
|
||||
</DeleteConfirmationDialog>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</CardContent>
|
||||
<CardFooter>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
บัญชีทุกบัญชีมีสิทธิ์ผู้ดูแลระบบ
|
||||
</p>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user