From aca3667a9d125de9c2c452aed87bb89f3ceae8d1 Mon Sep 17 00:00:00 2001 From: admin Date: Mon, 29 Sep 2025 08:39:12 +0700 Subject: [PATCH] =?UTF-8?q?Update=20login=20page.jsx=20=E0=B8=84=E0=B8=A3?= =?UTF-8?q?=E0=B8=B1=E0=B9=89=E0=B8=87=E0=B8=97=E0=B8=B5=E0=B9=88=202?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/app/(auth)/login/page.jsx | 364 ++--------------------------- 1 file changed, 19 insertions(+), 345 deletions(-) diff --git a/frontend/app/(auth)/login/page.jsx b/frontend/app/(auth)/login/page.jsx index 0b4e504f..8e35b373 100755 --- a/frontend/app/(auth)/login/page.jsx +++ b/frontend/app/(auth)/login/page.jsx @@ -1,8 +1,7 @@ -// frontend/app/(auth)/login/page.jsx "use client"; -import { useMemo, useState } from "react"; -import { useRouter, useSearchParams } from "next/navigation"; +import { useState, useMemo } from "react"; +import { useSearchParams, useRouter } from "next/navigation"; import { Card, CardHeader, @@ -15,24 +14,14 @@ 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"; -import { Separator } from "@/components/ui/separator"; - -const IS_DEV = process.env.NODE_ENV !== "production"; - -// URL builder กันเคสซ้ำ /api"use client"; - -import { useState, useMemo } 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"; export default function LoginPage() { const router = useRouter(); const searchParams = useSearchParams(); - const nextPath = useMemo(() => searchParams.get("next") || "/dashboard", [searchParams]); + const nextPath = useMemo( + () => searchParams.get("next") || "/dashboard", + [searchParams] + ); const [username, setUsername] = useState(""); const [password, setPassword] = useState(""); @@ -54,13 +43,16 @@ export default function LoginPage() { setSubmitting(true); // เรียก backend ให้ตั้ง HttpOnly cookie: access_token - const res = await fetch(`${process.env.NEXT_PUBLIC_API_BASE}/api/auth/login`, { - method: "POST", - credentials: "include", // สำคัญ: รับ/ส่งคุกกี้ - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ username, password, remember }), - cache: "no-store", - }); + const res = await fetch( + `${process.env.NEXT_PUBLIC_API_BASE}/api/auth/login`, + { + method: "POST", + credentials: "include", // สำคัญ: รับ/ส่งคุกกี้ + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ username, password, remember }), + cache: "no-store", + } + ); if (!res.ok) { const data = await res.json().catch(() => ({})); @@ -80,7 +72,9 @@ export default function LoginPage() { return ( - เข้าสู่ระบบ + + เข้าสู่ระบบ + Document Management System • LCBP3 @@ -200,323 +194,3 @@ function Spinner() { ); } - -function buildLoginUrl() { - const base = (process.env.NEXT_PUBLIC_API_BASE || "").replace(/\/+$/, ""); - if (base.endsWith("/api")) return `${base}/auth/login`; - return `${base}/api/auth/login`; -} - -// helper: parse response body เป็น json หรือ text -async function parseBody(res) { - const text = await res.text(); - try { - return { raw: text, json: JSON.parse(text) }; - } catch { - return { raw: text, json: null }; - } -} - -// สร้างข้อความ debug ที่พร้อม copy -function stringifyDebug(debugInfo) { - try { - return JSON.stringify(debugInfo, null, 2); - } catch { - return String(debugInfo); - } -} - -export default function LoginPage() { - const router = useRouter(); - const search = useSearchParams(); - const redirectTo = search.get("from") || "/dashboard"; - - const [username, setUsername] = useState(""); - const [password, setPassword] = useState(""); - const [submitting, setSubmitting] = useState(false); - const [error, setError] = useState(""); - - // สำหรับ debug panel - const [debugInfo, setDebugInfo] = useState(null); - const [copyState, setCopyState] = useState({ copied: false, error: "" }); - - const loginUrl = useMemo(buildLoginUrl, [process.env.NEXT_PUBLIC_API_BASE]); - - async function onSubmit(e) { - e.preventDefault(); - setSubmitting(true); - setError(""); - if (IS_DEV) { - setDebugInfo(null); - setCopyState({ copied: false, error: "" }); - } - - try { - const res = await fetch(loginUrl, { - method: "POST", - headers: { "Content-Type": "application/json" }, - credentials: "include", - body: JSON.stringify({ username, password }), - }); - - if (!res.ok) { - const body = await parseBody(res); - - const apiErr = { - name: "ApiError", - status: res.status, - statusText: res.statusText, - body: body.json ?? body.raw, - message: (() => { - const msgFromJson = - (body.json && (body.json.error || body.json.message)) || null; - - if (res.status === 400) - return `Bad request: ${msgFromJson ?? res.statusText}`; - if (res.status === 401) - return `Unauthenticated: ${msgFromJson ?? "Invalid credentials"}`; - if (res.status === 403) - return `Forbidden: ${msgFromJson ?? res.statusText}`; - if (res.status === 404) - return `Not found: ${msgFromJson ?? res.statusText}`; - if (res.status >= 500) - return `Server error (${res.status}): ${ - msgFromJson ?? res.statusText - }`; - return `${res.status} ${res.statusText}: ${ - msgFromJson ?? "Request failed" - }`; - })(), - }; - - if (IS_DEV) { - setDebugInfo({ - kind: "api", - request: { - url: loginUrl, - method: "POST", - payload: { username: "(masked)", password: "(masked)" }, - }, - response: { - status: res.status, - statusText: res.statusText, - body: apiErr.body, - }, - env: { - NEXT_PUBLIC_API_BASE: - process.env.NEXT_PUBLIC_API_BASE || "(unset)", - NODE_ENV: process.env.NODE_ENV, - }, - }); - } - - throw apiErr; - } - - // ✅ สำเร็จ - if (IS_DEV) { - setDebugInfo({ - kind: "success", - request: { url: loginUrl, method: "POST" }, - note: "Login success. Redirecting…", - }); - } - router.push(redirectTo); - } catch (err) { - if (err?.name === "ApiError") { - setError(err.message); - } else if (err instanceof TypeError && /fetch/i.test(err.message)) { - setError( - "Network error: ไม่สามารถเชื่อมต่อเซิร์ฟเวอร์ได้ (ตรวจสอบ proxy/NPM/SSL)" - ); - if (IS_DEV) { - setDebugInfo({ - kind: "network", - request: { url: loginUrl, method: "POST" }, - error: { message: err.message }, - hint: "เช็คว่า NPM ชี้ proxy /api ไปที่ backend ถูก network/port, และ TLS chain ถูกต้อง", - }); - } - } else { - setError(err?.message || "Unexpected error"); - if (IS_DEV) { - setDebugInfo({ - kind: "unknown", - request: { url: loginUrl, method: "POST" }, - error: { message: String(err) }, - }); - } - } - } finally { - setSubmitting(false); - } - } - - async function handleCopyDebug() { - if (!debugInfo) return; - const text = stringifyDebug(debugInfo); - try { - if (navigator.clipboard?.writeText) { - await navigator.clipboard.writeText(text); - } else { - // Fallback - const ta = document.createElement("textarea"); - ta.value = text; - ta.style.position = "fixed"; - ta.style.left = "-9999px"; - document.body.appendChild(ta); - ta.focus(); - ta.select(); - document.execCommand("copy"); - document.body.removeChild(ta); - } - setCopyState({ copied: true, error: "" }); - setTimeout(() => setCopyState({ copied: false, error: "" }), 1500); - } catch (e) { - setCopyState({ - copied: false, - error: "คัดลอกไม่สำเร็จ (permission ของ clipboard?)", - }); - setTimeout(() => setCopyState({ copied: false, error: "" }), 2500); - } - } - - return ( - - - Sign in - - Enter your credentials to access the DMS - - - - - {error ? ( - - {error} - - ) : null} - -
-
- - setUsername(e.target.value)} - required - /> -
- -
- - setPassword(e.target.value)} - required - /> -
- - -
- - {IS_DEV && ( -
-
-
- Debug (dev mode only) -
-
- -
-
- -
-
- Request URL:{" "} - {loginUrl} -
- - {debugInfo?.request?.method && ( -
- Method:{" "} - {debugInfo.request.method} -
- )} - - {debugInfo?.response && ( - <> -
- Status:{" "} - - {debugInfo.response.status}{" "} - {debugInfo.response.statusText} - -
-
Response body:
-
-                    {typeof debugInfo.response.body === "string"
-                      ? debugInfo.response.body
-                      : JSON.stringify(debugInfo.response.body, null, 2)}
-                  
- - )} - - {debugInfo?.error && ( - <> -
Error:
-
-                    {JSON.stringify(debugInfo.error, null, 2)}
-                  
- - )} - - {debugInfo?.env && ( - <> -
Env:
-
-                    {JSON.stringify(debugInfo.env, null, 2)}
-                  
- - )} - - {debugInfo?.note && ( -
{debugInfo.note}
- )} - {debugInfo?.hint && ( -
- Hint: {debugInfo.hint} -
- )} - - {copyState.error && ( -
{copyState.error}
- )} -
-
- )} -
- - - © {new Date().getFullYear()} np-dms.work - -
- ); -}