eCourt-note/app/note/annotate/[id]/page.tsx

75 lines
2.3 KiB
TypeScript
Raw Normal View History

import { asc, eq, inArray } from "drizzle-orm";
mock login e-Court หน้า /login + identity จาก cookie oacCase แทน localStorage — dropdown ผู้ใช้ MOCK_USERS (user1–user5, utils/user.ts pure module ให้ client+server ใช้ร่วม) รหัสผ่านกลาง Demo1234 ผ่าน server action login (เช็ครายชื่อฝั่ง server เป็น defense กัน hand-craft + delay 500ms ให้เห็น "กำลังตรวจสอบ…") ตั้ง cookie oacCase ตาม format จริงของระบบศาล 16 field — u_userid/u_userlogin ต่างกันต่อคน (= ชื่อผู้ใช้) เพื่อให้สิทธิ์ "แก้ไขได้เฉพาะของตัวเอง" (guard ด้วย userId) แยกกันจริงทั้ง 5 คน httpOnly/sameSite lax/8 ชม. + middleware.ts บังคับ login ทุกหน้า (parse จริงด้วย parseOacCase ไม่ใช่แค่มี cookie — ขยะ/ว่าง/ขาด field = redirect /login; matcher ยกเว้น /login, /api/*, /uploads/*, _next และ static ใน public/ — SSE/export จึงไม่โดน 302) + lib/session.ts getSessionUser อ่าน identity ฝั่ง server ใช้ในทุกหน้า (defense ซ้อน middleware) + PDFViewer รับ user เป็น prop จาก annotate page ตั้งแต่ first paint (หมดช่วง null user ที่เคยสร้าง annotation ด้วย userId="" ได้ + ไม่มี hydration mismatch; Server Actions ไม่ต้องแก้ — vString รับ userId ใหม่อยู่แล้ว) + SessionBar แสดง "เข้าสู่ระบบเป็น <user>" + ปุ่มออกจากระบบ (ลบ cookie ด้วย path "/" ตรงตอน set + redirect ใน action เอง — client router.replace เองชนกับ re-render ของหน้าที่เพิ่งเสีย session จน navigation ไม่เกิด) แทน UserPicker mock 10 คน (ลบไฟล์พร้อม MOCK_USERS/localStorage/getLocalUser ทั้งชุด) + LoginForm การ์ด max-w-sm กลางจอ reuse brand lockup และปุ่ม primary เดิม, input text-base 16px กัน iOS zoom บน iPad, dropdown placeholder "เลือกผู้ใช้" บังคับเลือก (ไม่ preselect กัน login ผิดคนโดยไม่ตั้งใจ), error แถบเดียว role=alert "รหัสผ่านไม่ถูกต้อง" (dropdown เลือกผิดไม่ได้แล้ว), ตอนผิดล้าง password คงผู้ใช้ที่เลือกไว้ + focus+select + tests/utils/user.test.ts ล็อก format จริงของ e-Court (parse ตัวอย่าง cookie จริง 235821/tu_2, garbage/empty/ขาด field → null, round-trip, buildOacCaseCookie ครบ 16 field) + sync CLAUDE.md/PLAN.md §0 A4/README — annotation สมัย mock-user-01/UUID ไม่ใช่ของตัวเองอีก (auth จริงเทียบระบบศาล + roles ยัง out of scope; /api/* และ /uploads/* ยังไม่ต้อง login ตามขอบเขต mock) Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-17 13:41:02 +07:00
import { notFound, redirect } from "next/navigation";
import { db } from "@/db";
import {
annotationReplies,
annotations,
bookmarks,
files,
toAnnotationDTO,
toBookmarkDTO,
toReplyDTO,
} from "@/db/schema";
import PDFViewer from "@/components/pdf/PDFViewer";
ไฮไลต์เสร็จแล้ว popup ซ้อนกันอีก 2 รูปแบบ (tooltip ทับแผงบน desktop + เมนู tool ค้างเปิดบน iOS) + งาน TEMP-DEBUG ของปัญหา pencil ลากไม่ติดบน iPad: ส่วนที่ 1 — แก้ popup ซ้อนตอนสร้างไฮไลต์ (bug report: กด tool highlight → ไป highlight → เสร็จแล้วมี popup ของแก้ไขแสดงอีกอัน): (1) Tooltip hover (N1) ซ่อนเมื่อรายการที่ชี้ = selectedId — เดิมลากคลุมจบแล้วเคอร์เซอร์ยังอยู่บนไฮไลต์ใหม่พอดี auto-select เปิดแผง selection แล้ว tooltip ยังโผล่ซ้อนอีกกล่องบนตำแหน่งเดียวกัน; ชี้รายการอื่นที่ไม่ได้เลือก tooltip ยังทำงานตามเดิม (2) handleCreateAnnotation ตรง branch AUTO_SELECT_TYPES ปิดเมนู dropdown ทั้ง 5 + popup bookmark ก่อนแผงโผล่ — บน iOS การลาก (คลุมข้อความ/วาดเส้น) ไม่ยิง compat mousedown ให้ outside-click ปิดเมนูเอง (Android/desktop ปิดตั้งแต่เริ่มกดแล้ว) ไม่ปิดตรงนี้ = เมนูค้างเปิดซ้อนแผงบนจอสัมผัส - sync rules/pdf-viewer.md: กติกา popup ทีละชุดเพิ่มกรณี auto-select ปิดเมนู + เงื่อนไขซ่อน tooltip เมื่อ hovered = selectedId - verify: tsc ผ่าน + Playwright 6/6 — desktop: ไฮไลต์เสร็จแผงโผล่/ไม่มี tooltip ซ้อน/ชี้รายการอื่น tooltip กลับมา; เลียน iOS (dispatch touch PointerEvent ล้วนไม่มี compat mousedown): ลากคลุม commit จริง + แผงโผล่ + เมนูที่เคยเปิดค้างถูกปิด ส่วนที่ 2 — TEMP-DEBUG ปัญหา pencil ลากแล้วบางทีไม่ commit บน iPad (ทำคู่กันระหว่าง debug จริงเครื่อง ยังไม่ถึงเวลาเก็บกวาด): - components/DebugConsole.tsx (ใหม่): เปิด Eruda จาก CDN เมื่อ URL มี ?debug — อ่าน console บนจอ iPad จริงโดยไม่ต้องต่อ Safari Web Inspector (โหลดพลาดมีแบนเนอร์แจ้ง) + mount ใน app/annotate/[id]/page.tsx — ทั้งคู่ mark TEMP-DEBUG ไว้ลบพร้อมกัน - hooks/useCustomSelection.ts: log [debug] ตำแหน่งสำคัญของ gesture (pointerdown / move แรกของ pointerId นั้นหลัง down — กัน log 60-120Hz ท่วม console จนเปลี่ยนพฤติกรรมที่ดูอยู่เอง / แยก pointerup vs pointercancel / นิ้วที่สองถูกเมิน) + เก็บความเห็นใหม่ว่า guard นิ้วที่สองครอบคลุม touch ปลอมจาก palm rejection (มือพักบนจอระหว่างลากด้วย pen) - components/UploadFileForm.tsx: ปุ่มอัปโหลด ml-auto ชิดขวา (เดิมติด input ไปฝั่งซ้าย) Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-15 15:10:27 +07:00
import DebugConsole from "@/components/DebugConsole"; // TEMP-DEBUG (ลบพร้อมไฟล์)
mock login e-Court หน้า /login + identity จาก cookie oacCase แทน localStorage — dropdown ผู้ใช้ MOCK_USERS (user1–user5, utils/user.ts pure module ให้ client+server ใช้ร่วม) รหัสผ่านกลาง Demo1234 ผ่าน server action login (เช็ครายชื่อฝั่ง server เป็น defense กัน hand-craft + delay 500ms ให้เห็น "กำลังตรวจสอบ…") ตั้ง cookie oacCase ตาม format จริงของระบบศาล 16 field — u_userid/u_userlogin ต่างกันต่อคน (= ชื่อผู้ใช้) เพื่อให้สิทธิ์ "แก้ไขได้เฉพาะของตัวเอง" (guard ด้วย userId) แยกกันจริงทั้ง 5 คน httpOnly/sameSite lax/8 ชม. + middleware.ts บังคับ login ทุกหน้า (parse จริงด้วย parseOacCase ไม่ใช่แค่มี cookie — ขยะ/ว่าง/ขาด field = redirect /login; matcher ยกเว้น /login, /api/*, /uploads/*, _next และ static ใน public/ — SSE/export จึงไม่โดน 302) + lib/session.ts getSessionUser อ่าน identity ฝั่ง server ใช้ในทุกหน้า (defense ซ้อน middleware) + PDFViewer รับ user เป็น prop จาก annotate page ตั้งแต่ first paint (หมดช่วง null user ที่เคยสร้าง annotation ด้วย userId="" ได้ + ไม่มี hydration mismatch; Server Actions ไม่ต้องแก้ — vString รับ userId ใหม่อยู่แล้ว) + SessionBar แสดง "เข้าสู่ระบบเป็น <user>" + ปุ่มออกจากระบบ (ลบ cookie ด้วย path "/" ตรงตอน set + redirect ใน action เอง — client router.replace เองชนกับ re-render ของหน้าที่เพิ่งเสีย session จน navigation ไม่เกิด) แทน UserPicker mock 10 คน (ลบไฟล์พร้อม MOCK_USERS/localStorage/getLocalUser ทั้งชุด) + LoginForm การ์ด max-w-sm กลางจอ reuse brand lockup และปุ่ม primary เดิม, input text-base 16px กัน iOS zoom บน iPad, dropdown placeholder "เลือกผู้ใช้" บังคับเลือก (ไม่ preselect กัน login ผิดคนโดยไม่ตั้งใจ), error แถบเดียว role=alert "รหัสผ่านไม่ถูกต้อง" (dropdown เลือกผิดไม่ได้แล้ว), ตอนผิดล้าง password คงผู้ใช้ที่เลือกไว้ + focus+select + tests/utils/user.test.ts ล็อก format จริงของ e-Court (parse ตัวอย่าง cookie จริง 235821/tu_2, garbage/empty/ขาด field → null, round-trip, buildOacCaseCookie ครบ 16 field) + sync CLAUDE.md/PLAN.md §0 A4/README — annotation สมัย mock-user-01/UUID ไม่ใช่ของตัวเองอีก (auth จริงเทียบระบบศาล + roles ยัง out of scope; /api/* และ /uploads/* ยังไม่ต้อง login ตามขอบเขต mock) Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-17 13:41:02 +07:00
import { getSessionUser } from "@/lib/session";
const UUID_RE =
/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
export const dynamic = "force-dynamic";
export default async function AnnotatePage({
params,
}: {
params: { id: string };
}) {
const { id } = params;
if (!UUID_RE.test(id)) notFound();
mock login e-Court หน้า /login + identity จาก cookie oacCase แทน localStorage — dropdown ผู้ใช้ MOCK_USERS (user1–user5, utils/user.ts pure module ให้ client+server ใช้ร่วม) รหัสผ่านกลาง Demo1234 ผ่าน server action login (เช็ครายชื่อฝั่ง server เป็น defense กัน hand-craft + delay 500ms ให้เห็น "กำลังตรวจสอบ…") ตั้ง cookie oacCase ตาม format จริงของระบบศาล 16 field — u_userid/u_userlogin ต่างกันต่อคน (= ชื่อผู้ใช้) เพื่อให้สิทธิ์ "แก้ไขได้เฉพาะของตัวเอง" (guard ด้วย userId) แยกกันจริงทั้ง 5 คน httpOnly/sameSite lax/8 ชม. + middleware.ts บังคับ login ทุกหน้า (parse จริงด้วย parseOacCase ไม่ใช่แค่มี cookie — ขยะ/ว่าง/ขาด field = redirect /login; matcher ยกเว้น /login, /api/*, /uploads/*, _next และ static ใน public/ — SSE/export จึงไม่โดน 302) + lib/session.ts getSessionUser อ่าน identity ฝั่ง server ใช้ในทุกหน้า (defense ซ้อน middleware) + PDFViewer รับ user เป็น prop จาก annotate page ตั้งแต่ first paint (หมดช่วง null user ที่เคยสร้าง annotation ด้วย userId="" ได้ + ไม่มี hydration mismatch; Server Actions ไม่ต้องแก้ — vString รับ userId ใหม่อยู่แล้ว) + SessionBar แสดง "เข้าสู่ระบบเป็น <user>" + ปุ่มออกจากระบบ (ลบ cookie ด้วย path "/" ตรงตอน set + redirect ใน action เอง — client router.replace เองชนกับ re-render ของหน้าที่เพิ่งเสีย session จน navigation ไม่เกิด) แทน UserPicker mock 10 คน (ลบไฟล์พร้อม MOCK_USERS/localStorage/getLocalUser ทั้งชุด) + LoginForm การ์ด max-w-sm กลางจอ reuse brand lockup และปุ่ม primary เดิม, input text-base 16px กัน iOS zoom บน iPad, dropdown placeholder "เลือกผู้ใช้" บังคับเลือก (ไม่ preselect กัน login ผิดคนโดยไม่ตั้งใจ), error แถบเดียว role=alert "รหัสผ่านไม่ถูกต้อง" (dropdown เลือกผิดไม่ได้แล้ว), ตอนผิดล้าง password คงผู้ใช้ที่เลือกไว้ + focus+select + tests/utils/user.test.ts ล็อก format จริงของ e-Court (parse ตัวอย่าง cookie จริง 235821/tu_2, garbage/empty/ขาด field → null, round-trip, buildOacCaseCookie ครบ 16 field) + sync CLAUDE.md/PLAN.md §0 A4/README — annotation สมัย mock-user-01/UUID ไม่ใช่ของตัวเองอีก (auth จริงเทียบระบบศาล + roles ยัง out of scope; /api/* และ /uploads/* ยังไม่ต้อง login ตามขอบเขต mock) Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-17 13:41:02 +07:00
// defense ซ้อน middleware — identity จาก cookie oacCase ส่งเข้า viewer ตั้งแต่ first paint
const user = getSessionUser();
refactor(routes): move routes under /note, APIs under /api/note เตรียม basePath /docedit เป็นพื้นที่กลางให้แอปพี่น้อง (wordaddin ใน อนาคต) — เดิม basePath ครอบ /docedit/note ทั้งหมดจนแชร์ prefix ไม่ได้ (Next มี basePath ได้ทางเดียว) และจัด API แยกต้นไม้ตามที่ผู้ใช้ขอ (1) โครงสร้างใหม่: basePath = /docedit + route หน้าทั้งหมดย้ายเข้า app/note/** + API 4 เส้นย้ายเข้า app/api/note/** + statics ย้ายเข้า public/note/** (samples, uploads, pdf.worker) — หน้า/statics URL ปลายทาง คงเดิมเป๊ะ (/docedit/note/…) ส่วน URL API เปลี่ยนเป็น /docedit/api/note/… ตามคำขอผู้ใช้ (N10 endpoint เปลี่ยน — ต้องแจ้ง backend e-Court ก่อนใช้) (2) กลไก: withBasePath (utils/base-path.ts) เติม base + app path /note ยกเว้น path ที่ขึ้นต้น /api/ เติมแค่ base (caller เขียน /api/note/… เต็ม); resolvePublicPdfPath root ที่ public/note (traversal guard เดิม); middleware matcher ยกเว้นใหม่ (note/login, api/note, note/uploads, note/samples, note/pdf.worker, _next, fonts, favicon) + redirect /note/login; next.config เพิ่ม redirects /docedit -> /docedit/note; navigation ภายใน 19 จุด + revalidatePath 3 จุด ใส่ prefix /note; pdf_url ใน DB เป็น logical path ไม่แก้แม้แต่ตัวเดียว (seed/upload เขียนค่าเดิม, browser เติมผ่าน withBasePath, disk เติมผ่าน guard) (3) ตามแก้: patch-pdf-worker (DEST public/note + mkdirSync ปลายทาง), seed-samples (SAMPLES_DIR), uploadFile (UPLOAD_DIR), Dockerfile/ .dockerignore/.gitignore/docker-compose.test.yml, tests 2 ไฟล์ที่อ้าง disk path จริง, sync เอกสารทั้งหมด (CLAUDE.md invariant #6/#12 + tree, README, docs/API-DOCUMENTS.md, .claude/rules/*, sync-db skill) (4) !! ตอน deploy ต้องย้าย volume ตาม: mount /app/public/uploads -> /app/public/note/uploads + ย้ายไฟล์ใน volume ให้ตรงตำแหน่ง ไม่งั้นไฟล์ ที่เคยอัปโหลด (พร้อม annotation ที่ผูกอยู่) 404 ทันทีที่สลับ image (5) verify: build ผ่าน (route table /api/note/** + /note/** ครบ), tsc, vitest 215/215, curl ครบทุก endpoint (login/redirect chain, worker, samples, uploads, SSE, api/pdf, export, N8 register, N10 bundle + deep link), Playwright browser จริง (canvas + textLayer ไทย render, browser ยิง URL ใหม่ทั้งหมดไม่มี request ไป URL เก่า), plan-reviewer + code-reviewer 3 รายงาน 0 blocking Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-10-06 14:55:07 +07:00
if (!user) redirect("/note/login");
mock login e-Court หน้า /login + identity จาก cookie oacCase แทน localStorage — dropdown ผู้ใช้ MOCK_USERS (user1–user5, utils/user.ts pure module ให้ client+server ใช้ร่วม) รหัสผ่านกลาง Demo1234 ผ่าน server action login (เช็ครายชื่อฝั่ง server เป็น defense กัน hand-craft + delay 500ms ให้เห็น "กำลังตรวจสอบ…") ตั้ง cookie oacCase ตาม format จริงของระบบศาล 16 field — u_userid/u_userlogin ต่างกันต่อคน (= ชื่อผู้ใช้) เพื่อให้สิทธิ์ "แก้ไขได้เฉพาะของตัวเอง" (guard ด้วย userId) แยกกันจริงทั้ง 5 คน httpOnly/sameSite lax/8 ชม. + middleware.ts บังคับ login ทุกหน้า (parse จริงด้วย parseOacCase ไม่ใช่แค่มี cookie — ขยะ/ว่าง/ขาด field = redirect /login; matcher ยกเว้น /login, /api/*, /uploads/*, _next และ static ใน public/ — SSE/export จึงไม่โดน 302) + lib/session.ts getSessionUser อ่าน identity ฝั่ง server ใช้ในทุกหน้า (defense ซ้อน middleware) + PDFViewer รับ user เป็น prop จาก annotate page ตั้งแต่ first paint (หมดช่วง null user ที่เคยสร้าง annotation ด้วย userId="" ได้ + ไม่มี hydration mismatch; Server Actions ไม่ต้องแก้ — vString รับ userId ใหม่อยู่แล้ว) + SessionBar แสดง "เข้าสู่ระบบเป็น <user>" + ปุ่มออกจากระบบ (ลบ cookie ด้วย path "/" ตรงตอน set + redirect ใน action เอง — client router.replace เองชนกับ re-render ของหน้าที่เพิ่งเสีย session จน navigation ไม่เกิด) แทน UserPicker mock 10 คน (ลบไฟล์พร้อม MOCK_USERS/localStorage/getLocalUser ทั้งชุด) + LoginForm การ์ด max-w-sm กลางจอ reuse brand lockup และปุ่ม primary เดิม, input text-base 16px กัน iOS zoom บน iPad, dropdown placeholder "เลือกผู้ใช้" บังคับเลือก (ไม่ preselect กัน login ผิดคนโดยไม่ตั้งใจ), error แถบเดียว role=alert "รหัสผ่านไม่ถูกต้อง" (dropdown เลือกผิดไม่ได้แล้ว), ตอนผิดล้าง password คงผู้ใช้ที่เลือกไว้ + focus+select + tests/utils/user.test.ts ล็อก format จริงของ e-Court (parse ตัวอย่าง cookie จริง 235821/tu_2, garbage/empty/ขาด field → null, round-trip, buildOacCaseCookie ครบ 16 field) + sync CLAUDE.md/PLAN.md §0 A4/README — annotation สมัย mock-user-01/UUID ไม่ใช่ของตัวเองอีก (auth จริงเทียบระบบศาล + roles ยัง out of scope; /api/* และ /uploads/* ยังไม่ต้อง login ตามขอบเขต mock) Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-17 13:41:02 +07:00
const [file] = await db.select().from(files).where(eq(files.id, id));
if (!file) notFound();
const [annotationRows, bookmarkRows] = await Promise.all([
db
.select()
.from(annotations)
.where(eq(annotations.fileId, id))
.orderBy(asc(annotations.createdAt)),
db
.select()
.from(bookmarks)
.where(eq(bookmarks.fileId, id))
.orderBy(asc(bookmarks.createdAt)),
]);
// replies ของทุก comment ในไฟล์นี้ (F9) — แม่ถูก CASCADE ไปแล้ว row ก็หายตาม
const annotationIds = annotationRows.map((r) => r.id);
const replyRows = annotationIds.length
? await db
.select()
.from(annotationReplies)
.where(inArray(annotationReplies.annotationId, annotationIds))
.orderBy(asc(annotationReplies.createdAt))
: [];
return (
ไฮไลต์เสร็จแล้ว popup ซ้อนกันอีก 2 รูปแบบ (tooltip ทับแผงบน desktop + เมนู tool ค้างเปิดบน iOS) + งาน TEMP-DEBUG ของปัญหา pencil ลากไม่ติดบน iPad: ส่วนที่ 1 — แก้ popup ซ้อนตอนสร้างไฮไลต์ (bug report: กด tool highlight → ไป highlight → เสร็จแล้วมี popup ของแก้ไขแสดงอีกอัน): (1) Tooltip hover (N1) ซ่อนเมื่อรายการที่ชี้ = selectedId — เดิมลากคลุมจบแล้วเคอร์เซอร์ยังอยู่บนไฮไลต์ใหม่พอดี auto-select เปิดแผง selection แล้ว tooltip ยังโผล่ซ้อนอีกกล่องบนตำแหน่งเดียวกัน; ชี้รายการอื่นที่ไม่ได้เลือก tooltip ยังทำงานตามเดิม (2) handleCreateAnnotation ตรง branch AUTO_SELECT_TYPES ปิดเมนู dropdown ทั้ง 5 + popup bookmark ก่อนแผงโผล่ — บน iOS การลาก (คลุมข้อความ/วาดเส้น) ไม่ยิง compat mousedown ให้ outside-click ปิดเมนูเอง (Android/desktop ปิดตั้งแต่เริ่มกดแล้ว) ไม่ปิดตรงนี้ = เมนูค้างเปิดซ้อนแผงบนจอสัมผัส - sync rules/pdf-viewer.md: กติกา popup ทีละชุดเพิ่มกรณี auto-select ปิดเมนู + เงื่อนไขซ่อน tooltip เมื่อ hovered = selectedId - verify: tsc ผ่าน + Playwright 6/6 — desktop: ไฮไลต์เสร็จแผงโผล่/ไม่มี tooltip ซ้อน/ชี้รายการอื่น tooltip กลับมา; เลียน iOS (dispatch touch PointerEvent ล้วนไม่มี compat mousedown): ลากคลุม commit จริง + แผงโผล่ + เมนูที่เคยเปิดค้างถูกปิด ส่วนที่ 2 — TEMP-DEBUG ปัญหา pencil ลากแล้วบางทีไม่ commit บน iPad (ทำคู่กันระหว่าง debug จริงเครื่อง ยังไม่ถึงเวลาเก็บกวาด): - components/DebugConsole.tsx (ใหม่): เปิด Eruda จาก CDN เมื่อ URL มี ?debug — อ่าน console บนจอ iPad จริงโดยไม่ต้องต่อ Safari Web Inspector (โหลดพลาดมีแบนเนอร์แจ้ง) + mount ใน app/annotate/[id]/page.tsx — ทั้งคู่ mark TEMP-DEBUG ไว้ลบพร้อมกัน - hooks/useCustomSelection.ts: log [debug] ตำแหน่งสำคัญของ gesture (pointerdown / move แรกของ pointerId นั้นหลัง down — กัน log 60-120Hz ท่วม console จนเปลี่ยนพฤติกรรมที่ดูอยู่เอง / แยก pointerup vs pointercancel / นิ้วที่สองถูกเมิน) + เก็บความเห็นใหม่ว่า guard นิ้วที่สองครอบคลุม touch ปลอมจาก palm rejection (มือพักบนจอระหว่างลากด้วย pen) - components/UploadFileForm.tsx: ปุ่มอัปโหลด ml-auto ชิดขวา (เดิมติด input ไปฝั่งซ้าย) Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-15 15:10:27 +07:00
<>
{/* TEMP-DEBUG: อ่าน log บนจอ iPad ผ่าน Eruda เมื่อเปิดหน้าพร้อม ?debug */}
<DebugConsole />
<PDFViewer
file={{ id: file.id, title: file.title, pdfUrl: file.pdfUrl }}
initialAnnotations={annotationRows.map(toAnnotationDTO)}
initialBookmarks={bookmarkRows.map(toBookmarkDTO)}
initialReplies={replyRows.map(toReplyDTO)}
mock login e-Court หน้า /login + identity จาก cookie oacCase แทน localStorage — dropdown ผู้ใช้ MOCK_USERS (user1–user5, utils/user.ts pure module ให้ client+server ใช้ร่วม) รหัสผ่านกลาง Demo1234 ผ่าน server action login (เช็ครายชื่อฝั่ง server เป็น defense กัน hand-craft + delay 500ms ให้เห็น "กำลังตรวจสอบ…") ตั้ง cookie oacCase ตาม format จริงของระบบศาล 16 field — u_userid/u_userlogin ต่างกันต่อคน (= ชื่อผู้ใช้) เพื่อให้สิทธิ์ "แก้ไขได้เฉพาะของตัวเอง" (guard ด้วย userId) แยกกันจริงทั้ง 5 คน httpOnly/sameSite lax/8 ชม. + middleware.ts บังคับ login ทุกหน้า (parse จริงด้วย parseOacCase ไม่ใช่แค่มี cookie — ขยะ/ว่าง/ขาด field = redirect /login; matcher ยกเว้น /login, /api/*, /uploads/*, _next และ static ใน public/ — SSE/export จึงไม่โดน 302) + lib/session.ts getSessionUser อ่าน identity ฝั่ง server ใช้ในทุกหน้า (defense ซ้อน middleware) + PDFViewer รับ user เป็น prop จาก annotate page ตั้งแต่ first paint (หมดช่วง null user ที่เคยสร้าง annotation ด้วย userId="" ได้ + ไม่มี hydration mismatch; Server Actions ไม่ต้องแก้ — vString รับ userId ใหม่อยู่แล้ว) + SessionBar แสดง "เข้าสู่ระบบเป็น <user>" + ปุ่มออกจากระบบ (ลบ cookie ด้วย path "/" ตรงตอน set + redirect ใน action เอง — client router.replace เองชนกับ re-render ของหน้าที่เพิ่งเสีย session จน navigation ไม่เกิด) แทน UserPicker mock 10 คน (ลบไฟล์พร้อม MOCK_USERS/localStorage/getLocalUser ทั้งชุด) + LoginForm การ์ด max-w-sm กลางจอ reuse brand lockup และปุ่ม primary เดิม, input text-base 16px กัน iOS zoom บน iPad, dropdown placeholder "เลือกผู้ใช้" บังคับเลือก (ไม่ preselect กัน login ผิดคนโดยไม่ตั้งใจ), error แถบเดียว role=alert "รหัสผ่านไม่ถูกต้อง" (dropdown เลือกผิดไม่ได้แล้ว), ตอนผิดล้าง password คงผู้ใช้ที่เลือกไว้ + focus+select + tests/utils/user.test.ts ล็อก format จริงของ e-Court (parse ตัวอย่าง cookie จริง 235821/tu_2, garbage/empty/ขาด field → null, round-trip, buildOacCaseCookie ครบ 16 field) + sync CLAUDE.md/PLAN.md §0 A4/README — annotation สมัย mock-user-01/UUID ไม่ใช่ของตัวเองอีก (auth จริงเทียบระบบศาล + roles ยัง out of scope; /api/* และ /uploads/* ยังไม่ต้อง login ตามขอบเขต mock) Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-17 13:41:02 +07:00
user={user}
ไฮไลต์เสร็จแล้ว popup ซ้อนกันอีก 2 รูปแบบ (tooltip ทับแผงบน desktop + เมนู tool ค้างเปิดบน iOS) + งาน TEMP-DEBUG ของปัญหา pencil ลากไม่ติดบน iPad: ส่วนที่ 1 — แก้ popup ซ้อนตอนสร้างไฮไลต์ (bug report: กด tool highlight → ไป highlight → เสร็จแล้วมี popup ของแก้ไขแสดงอีกอัน): (1) Tooltip hover (N1) ซ่อนเมื่อรายการที่ชี้ = selectedId — เดิมลากคลุมจบแล้วเคอร์เซอร์ยังอยู่บนไฮไลต์ใหม่พอดี auto-select เปิดแผง selection แล้ว tooltip ยังโผล่ซ้อนอีกกล่องบนตำแหน่งเดียวกัน; ชี้รายการอื่นที่ไม่ได้เลือก tooltip ยังทำงานตามเดิม (2) handleCreateAnnotation ตรง branch AUTO_SELECT_TYPES ปิดเมนู dropdown ทั้ง 5 + popup bookmark ก่อนแผงโผล่ — บน iOS การลาก (คลุมข้อความ/วาดเส้น) ไม่ยิง compat mousedown ให้ outside-click ปิดเมนูเอง (Android/desktop ปิดตั้งแต่เริ่มกดแล้ว) ไม่ปิดตรงนี้ = เมนูค้างเปิดซ้อนแผงบนจอสัมผัส - sync rules/pdf-viewer.md: กติกา popup ทีละชุดเพิ่มกรณี auto-select ปิดเมนู + เงื่อนไขซ่อน tooltip เมื่อ hovered = selectedId - verify: tsc ผ่าน + Playwright 6/6 — desktop: ไฮไลต์เสร็จแผงโผล่/ไม่มี tooltip ซ้อน/ชี้รายการอื่น tooltip กลับมา; เลียน iOS (dispatch touch PointerEvent ล้วนไม่มี compat mousedown): ลากคลุม commit จริง + แผงโผล่ + เมนูที่เคยเปิดค้างถูกปิด ส่วนที่ 2 — TEMP-DEBUG ปัญหา pencil ลากแล้วบางทีไม่ commit บน iPad (ทำคู่กันระหว่าง debug จริงเครื่อง ยังไม่ถึงเวลาเก็บกวาด): - components/DebugConsole.tsx (ใหม่): เปิด Eruda จาก CDN เมื่อ URL มี ?debug — อ่าน console บนจอ iPad จริงโดยไม่ต้องต่อ Safari Web Inspector (โหลดพลาดมีแบนเนอร์แจ้ง) + mount ใน app/annotate/[id]/page.tsx — ทั้งคู่ mark TEMP-DEBUG ไว้ลบพร้อมกัน - hooks/useCustomSelection.ts: log [debug] ตำแหน่งสำคัญของ gesture (pointerdown / move แรกของ pointerId นั้นหลัง down — กัน log 60-120Hz ท่วม console จนเปลี่ยนพฤติกรรมที่ดูอยู่เอง / แยก pointerup vs pointercancel / นิ้วที่สองถูกเมิน) + เก็บความเห็นใหม่ว่า guard นิ้วที่สองครอบคลุม touch ปลอมจาก palm rejection (มือพักบนจอระหว่างลากด้วย pen) - components/UploadFileForm.tsx: ปุ่มอัปโหลด ml-auto ชิดขวา (เดิมติด input ไปฝั่งซ้าย) Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-15 15:10:27 +07:00
/>
</>
);
}