// File: frontend/app/(auth)/login/page.jsx "use client"; // ✅ ปรับให้ตรง backend: ใช้ Bearer token (ไม่ใช้ cookie) // - เรียก POST /api/auth/login → รับ { token, refresh_token, user } // - เก็บ token/refresh_token ใน localStorage (หรือ sessionStorage ถ้าไม่ติ๊กจำไว้) // - ไม่ใช้ credentials: "include" อีกต่อไป // - เอา RootLayout/metadata ออก เพราะไฟล์เพจเป็น client component // - เพิ่มการอ่าน NEXT_PUBLIC_API_BASE และ error handling ให้ตรงกับ backend import { useState, useMemo, Suspense } from "react"; import { useSearchParams, useRouter } from "next/navigation"; import { Card, CardHeader, CardTitle, CardDescription, CardContent, CardFooter, } from "@/components/ui/card"; import { Label } from "@/components/ui/label"; import { Input } from "@/components/ui/input"; import { Button } from "@/components/ui/button"; import { Alert, AlertDescription } from "@/components/ui/alert"; const API_BASE = process.env.NEXT_PUBLIC_API_BASE?.replace(/\/$/, "") || ""; function LoginForm() { const router = useRouter(); const searchParams = useSearchParams(); const nextPath = useMemo( () => searchParams.get("next") || "/dashboard", [searchParams] ); const [username, setUsername] = useState(""); const [password, setPassword] = useState(""); const [showPw, setShowPw] = useState(false); const [remember, setRemember] = useState(false); const [submitting, setSubmitting] = useState(false); const [err, setErr] = useState(""); async function onSubmit(e) { e.preventDefault(); setErr(""); if (!username.trim() || !password) { setErr("กรอกชื่อผู้ใช้และรหัสผ่านให้ครบ"); return; } try { setSubmitting(true); const res = await fetch(`${API_BASE}/api/auth/login`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ username, password }), cache: "no-store", }); const data = await res.json().catch(() => ({})); if (!res.ok) { // รองรับข้อความ error จาก backend เช่น INVALID_CREDENTIALS setErr( data?.error === "INVALID_CREDENTIALS" ? "ชื่อผู้ใช้หรือรหัสผ่านไม่ถูกต้อง" : data?.error || "เข้าสู่ระบบไม่สำเร็จ" ); return; } // ✅ เก็บ token ตามโหมดจำไว้/ไม่จำ const storage = remember ? window.localStorage : window.sessionStorage; storage.setItem("dms.token", data.token); storage.setItem("dms.refresh_token", data.refresh_token); storage.setItem("dms.user", JSON.stringify(data.user || {})); // (ออปชัน) เผยแพร่ event ให้แท็บอื่นทราบ try { window.dispatchEvent( new StorageEvent("storage", { key: "dms.auth", newValue: "login" }) ); } catch {} router.replace(nextPath); } catch (e) { setErr("เชื่อมต่อเซิร์ฟเวอร์ไม่ได้ กรุณาลองใหม่"); } finally { setSubmitting(false); } } return (
เข้าสู่ระบบ Document Management System • LCBP3 {err ? ( {err} ) : null}
setUsername(e.target.value)} placeholder="เช่น superadmin" disabled={submitting} />
setPassword(e.target.value)} placeholder="••••••••" disabled={submitting} className="pr-10" />
ลืมรหัสผ่าน?
© {new Date().getFullYear()} np-dms.work
); } export default function LoginPage() { return ( }> ); } /** Loading skeleton */ function LoginPageSkeleton() { return (
เข้าสู่ระบบ Document Management System • LCBP3
); } /** Spinner แบบไม่พึ่งไลบรารีเสริม */ function Spinner() { return ( ); }