"use client"; // client gate ของหน้า /note/annotate/[id] (ฉบับ token 2026-10-30) — เดิมเป็น server // component อ่าน identity จาก cookie ผ่าน getSessionUser + query DB ตอน render: // ตอนนี้หน้าเป็น shell public ฝั่ง server (แค่ตรวจ UUID) ส่วนข้อมูลเริ่มต้น // (file + annotations + bookmarks + replies) ดึงผ่าน GET /api/note/files/[id]/ // bootstrap พร้อม Bearer หลังเห็น token ใน localStorage — ไม่มี/หมดอายุ = หน้า // gate "กลับ e-Court กดลิงก์ใหม่" (คู่ขนาน sso=required ของรอบ middleware) import Link from "next/link"; import { useEffect, useState } from "react"; import DebugConsole from "@/components/DebugConsole"; // TEMP-DEBUG (ลบพร้อมไฟล์) import { SSO_REQUIRED_BANNER } from "@/lib/note-auth"; import { clearNoteSession, noteAuthFetch, readNoteSession, type NoteSession, } from "@/lib/note-session"; import type { AnnotationDTO, BookmarkDTO, ReplyDTO } from "@/db/schema"; import PDFViewer from "@/components/pdf/PDFViewer"; type BootstrapData = { file: { id: string; title: string; pdfUrl: string }; annotations: AnnotationDTO[]; bookmarks: BookmarkDTO[]; replies: ReplyDTO[]; user: { userId: string; userName: string }; }; type Phase = "boot" | "loading" | "no-session" | "error" | "ready"; const BTN_CLS = "focus-ring inline-flex h-10 items-center rounded-md bg-primary px-5 text-sm font-medium text-white transition hover:-translate-y-px hover:bg-primary-hover hover:shadow-btn-glow"; function GateScreen({ title }: { title: string }) { return (
{SSO_REQUIRED_BANNER.text}

{title}

กลับหน้าหลัก
); } export default function AnnotateView({ fileId }: { fileId: string }) { const [phase, setPhase] = useState("boot"); const [data, setData] = useState(null); const [token, setToken] = useState(null); const [error, setError] = useState(null); useEffect(() => { const session: NoteSession | null = readNoteSession(); if (session === null) { setPhase("no-session"); return; } setToken(session.token); setPhase("loading"); let cancelled = false; void (async () => { try { const res = await noteAuthFetch(`/api/note/files/${fileId}/bootstrap`); if (res.status === 401) { // token หมดอายุ/ถูกเพิกถิอกลางวัน — ล้างทิ้งแล้วเป็น gate เหมือนไม่เคยเข้า clearNoteSession(); if (!cancelled) setPhase("no-session"); return; } const body = (await res.json().catch(() => null)) as | BootstrapData | { error?: string } | null; if (!res.ok || body === null || !("file" in body)) { if (!cancelled) { setError( (body as { error?: string } | null)?.error ?? `โหลดเอกสารไม่สำเร็จ (HTTP ${res.status})`, ); setPhase("error"); } return; } if (!cancelled) { setData(body); setPhase("ready"); } } catch (err) { if (cancelled) return; if (err instanceof Error && err.message === "no-session") { // session หมดพอดีระหว่างต้น effect กับตอน fetch — เป็น gate ไม่ใช่ network error clearNoteSession(); setPhase("no-session"); return; } setError("โหลดเอกสารไม่สำเร็จ — ตรวจการเชื่อมต่อแล้วลองใหม่อีกครั้ง"); setPhase("error"); } })(); return () => { cancelled = true; }; }, [fileId]); return ( <> {/* TEMP-DEBUG: อ่าน log บนจอ iPad ผ่าน Eruda เมื่อเปิดหน้าพร้อม ?debug */} {phase === "no-session" && ( )} {phase === "error" && (
{error}
กลับหน้าหลัก
)} {(phase === "boot" || phase === "loading") && (

กำลังโหลดเอกสาร…

)} {phase === "ready" && data !== null && token !== null && ( )} ); }