"use client"; // ฟอร์ม login (mock e-Court) — เรียก server action login แล้ว router.replace(next ?? "/") // (pattern ตาม UploadFileForm: useTransition + error state + ข้อความ fallback ตอน transport fail) // next = path วนกลับหลัง login (N8 D5 — ผ่าน sanitizeNextPath จากหน้า login มาแล้ว) import { useRef, useState, useTransition } from "react"; import { useRouter } from "next/navigation"; import { login } from "@/app/actions/auth"; import Select from "@/components/Select"; import { MOCK_USERS } from "@/utils/user"; // text input ของแอป — text-base (16px) เสมอ: กัน iOS auto-zoom ตอน focus บน iPad const INPUT_CLASS = "mt-1 block w-full rounded-md border border-slate-300 bg-white px-3 py-2 text-base text-slate-900 placeholder:text-slate-400 transition focus:border-primary focus:outline-none focus:ring-2 focus:ring-primary/30 disabled:cursor-not-allowed disabled:bg-slate-50 aria-invalid:border-red-600 aria-invalid:focus:ring-red-600/30"; // dropdown ผู้ใช้ — เหมือน INPUT_CLASS แต่ไม่มี mt-1 (Select ครอบ div.relative: มาร์จินที่ wrapper ข้างนอกแทน) const SELECT_CLASS = "block w-full rounded-md border border-slate-300 bg-white px-3 py-2 text-base text-slate-900 transition focus:border-primary focus:outline-none focus:ring-2 focus:ring-primary/30 disabled:cursor-not-allowed disabled:bg-slate-50"; export default function LoginForm({ next }: { next: string | null }) { const router = useRouter(); const passwordRef = useRef(null); const [error, setError] = useState(null); const [pending, startTransition] = useTransition(); const onSubmit = (e: React.FormEvent) => { e.preventDefault(); const form = e.currentTarget; setError(null); const usernameInput = form.elements.namedItem("username") as HTMLSelectElement; const passwordInput = form.elements.namedItem("password") as HTMLInputElement; startTransition(async () => { try { const result = await login(usernameInput.value, passwordInput.value); if (result.ok) { router.replace(next ?? "/"); } else { setError(result.error); // คง username ไว้ (โอกาสพลาดอยู่ที่ password) — ล้างแล้วโฟกัสให้พิมพ์ทับได้ทันที passwordInput.value = ""; passwordRef.current?.focus(); passwordRef.current?.select(); } } catch { setError("เข้าสู่ระบบไม่สำเร็จ — ลองใหม่อีกครั้ง"); } }); }; return (
{/* brand lockup เดียวกับ header หน้ารายการ (ลดขนาดตัวอักษรให้พอการ์ดแคบ) */}
JN

Judge's Note

ระบบ PDF Annotation สำหรับ e-Court

เข้าสู่ระบบ

{/* รหัสผ่านทดสอบ — รายชื่อเห็นใน dropdown แล้ว เหลือบอกแค่รหัสผ่าน (mock ไม่มีระบบสมัคร) */}

รหัสผ่านทดสอบ:{" "} Demo1234

{error && (

{error}

)}
); }