201 lines
7.0 KiB
TypeScript
201 lines
7.0 KiB
TypeScript
"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>
|
|
);
|
|
}
|