eCourt-note/components
Your Name f9114b3f00
All checks were successful
Deploy eCourt Note Web to Server / Validate Database Schema (Drizzle) (push) Successful in 4s
Deploy eCourt Note Web to Server / Build Web Docker Image (push) Successful in 46s
Deploy eCourt Note Web to Server / Deploy eCourt Note Web to Server (push) Successful in 4s
Deploy eCourt Note Web to Server / Notify Deployment Status (push) Successful in 1s
summary: หน้าสรุป design "แฟ้มสำนวน" + แก้ตาม design review 2026-09-17 — การ์ดรายหน้า 1 หน้า 1 การ์ด + สารบัญมือถือ + ghost buttons
งานหลักจาก design review (H/M/L ทั้งหมดยกเว้น L5 ที่จบเอง — หัวข้อ Explanation ถูกถอดไปก่อนหน้านี้ตามคำขอผู้ใช้ พร้อม icon ℹ + เส้น):
(1) H1 กฎ coarse 44px ครอบ button ด้วย (main.summary-page button min-height 2.75rem) — ปุ่มหุบ/กาง CollapsibleBlock เดิมหลุดกฎเพราะเป็น <button> ไม่ใช่ <a> สูงจริง ~29px
(2) H2 สารบัญมือถือ (< lg) — <details> การ์ด "สารบัญและผู้ร่วมทำ" จุดไข่ปลา + ผู้ร่วมทำภาษาเดียวกับ desktop zero JS (ได้ keyboard + screen reader ฟรี) แทนที่เดิมมือถือไม่มีทางกระโดดหน้าเลย
(3) H3 typography ขยายเพื่อผู้ใช้สูงวัย + อักษรไทย — เนื้อหาแถวรายการ text-[13px]→text-sm, โน้ต/badge text-[11px]→text-xs, บรรทัดสรุปหน้า→text-sm, "หน้า" text-[10px]→text-[11px], แถบ action→text-sm (CollapsibleBlock รวม)
(4) M1 aria-label anchor สารบัญ "หน้า N, X รายการ" กัน screen reader อ่านเลขติดกัน / M2 sticky aside max-h overflow (เอกสารหลายสิบหน้าครึ่งล่างเคยกดไม่ได้ตลอดกาล) / M3 ปุ่ม "เปิดหน้านี้" แสดงตลอดไม่ซ่อนบนมือถือ / M4 "ส่งออก CSV"→"ส่งออกสรุป (CSV)" คู่กับ "ส่งออก Bookmark (JSON)" (คง terminology อังกฤษตาม b1bc65e) ถอด title tooltip ที่จอสัมผัสใช้ไม่ได้ / M5 generateMetadata ชื่อแท็บ browser "สรุปหมายเหตุ — ชื่อคดี | Judge's Note"
(5) L2 CollapsibleBlock เพิ่ม useId + aria-controls/id ครบ disclosure pattern / L3 empty state CTA ใช้ RING_CLS ร่วม / L4 @media print (globals.css): header แปลงกรอบเส้นน้ำเงิน #0033a1 กัน text ขาวบนกระดาษขาว + break-inside ไม่ผ่าการ์ดกลางหน้ากระดาษ + ซ่อนแถบ action/สารบัญมือถือ / L1 แก้ comment หัวไฟล์ให้ตรงความจริง (มี client component เดียวคือ CollapsibleBlock)

ปรับตามคำขอผู้ใช้ระหว่างทาง (2026-09-17): เนื้อหาเปลี่ยนจากแผ่นกระดาษแผ่นเดียวคั่นเส้นเป็นการ์ดรายหน้า 1 หน้า 1 การ์ด (flex flex-col gap-4) / สารบัญ desktop เป็นการ์ด sticky / action bar เปลี่ยนลิงก์ข้อความเปล่าเป็น ghost button ขาวมีขอบ ภาษาเดียวกับการ์ด

งานคู่ขนานจากอีก session ในไฟล์ชุดนี้: แยก TypeIcon ออกจาก sidebar เป็น components/AnnotationTypeIcon.tsx (ไม่มี "use client") ใช้ร่วม sidebar + หน้าสรุป + ถอดหัวข้อคำอธิบายออกจากหน้าสรุป

CLAUDE.md sync โครงสร้าง/พฤติกรรมหน้าสรุปล่าสุด

verify: tsc strict ผ่าน + vitest 139/139 ผ่าน

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-17 19:48:58 +07:00
..
pdf bookmark: ดักความยาวชื่อ/โน้ตที่ช่องกรอก + F19 สรุปสิ่งที่เปลี่ยนใน dialog ประวัติ + formatThaiDate — (1) bookmark limits: utils/bookmark-limits.ts ใหม่เป็น source of truth เดียว (ชื่อ 120/โน้ต 200 ตัวอักษร) server action import ร่วมแทน const ท้องถิ่น + ใส่ maxLength ที่ popup บันทึก bookmark (PDFViewer) และฟอร์มแก้ไข ✏ ใน sidebar ดักทั้งพิมพ์/paste ตั้งแต่ในช่อง ไม่รอ error ตอนกดบันทึก + แก้ off-by-one ของ normalizeBookmarkTitle (เดิม slice 120 แล้วต่อ … = 121 ตัว ทำให้คลุมข้อความยาวแล้วกด B บันทึกเลยโดน limit ของ server ตีกลับทั้งที่ไม่ได้พิมพ์เอง — ตอนนี้ slice 119 + … = 120 พอดีตามสเปก §7 N2 ที่เขียนไว้ ≤ 120 อยู่แล้ว) + PLAN-FEAT.md §7 N2 บันทึกเพิ่ม — (2) งานคู่ขนาดจากอีก session (แชร์ไฟล์ PDFViewer/sidebar เลยรวม commit): dialog ประวัติ F19 แสดงสรุปสิ่งที่เปลี่ยนต่อรอบผ่าน describeAnnotationChange ใหม่ใน utils/annotation-display.ts (เทียบ snapshot ก่อนแก้ v.data กับ state ถัดไป — snapshot รอบใหม่กว่า หรือค่าปัจจุบันจาก realtime lookup สด historyCurrent — สรุปเป็นหน่วยเดียวกับตัวปรับ UI: ความหนา ×10 ความทึบ % fontSize px ชื่อสี preset ย้าย/ปรับขนาดจาก bbox ผลต่างเกิน GEOM_EPS_PCT 0.5% ข้อความ/โน้ต/label) + dialog ขยาย w-96 max-h-[60vh] แถวเปลี่ยน items-start + utils/date-format.ts ใหม่ formatThaiDate วันที่ไทยเต็ม "18 กันยายน พ.ศ. 2569" (+เวลา) แทน toLocaleString("th-TH") ทุกจุดแสดงวันที่ใน UI (หน้ารายการไฟล์ sidebar ประวัติ/รายการ timestamps ใน PDFViewer) กัน server/client locale ไม่ตรงกัน + tests ใหม่/ขยาย (date-format + describeAnnotationChange) — CLAUDE.md sync โครงสร้าง utils — verify: tsc strict ผ่าน + vitest 139/139 ผ่าน 2026-09-17 17:02:31 +07:00
sidebar summary: หน้าสรุป design "แฟ้มสำนวน" + แก้ตาม design review 2026-09-17 — การ์ดรายหน้า 1 หน้า 1 การ์ด + สารบัญมือถือ + ghost buttons 2026-09-17 19:48:58 +07:00
summary summary: หน้าสรุป design "แฟ้มสำนวน" + แก้ตาม design review 2026-09-17 — การ์ดรายหน้า 1 หน้า 1 การ์ด + สารบัญมือถือ + ghost buttons 2026-09-17 19:48:58 +07:00
AnnotationTypeIcon.tsx summary: หน้าสรุป design "แฟ้มสำนวน" + แก้ตาม design review 2026-09-17 — การ์ดรายหน้า 1 หน้า 1 การ์ด + สารบัญมือถือ + ghost buttons 2026-09-17 19:48:58 +07:00
DebugConsole.tsx fix(debug): DebugConsole 2026-09-15 16:18:53 +07:00
LoginForm.tsx 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) 2026-09-17 13:41:02 +07:00
LogoutButton.tsx 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) 2026-09-17 13:41:02 +07:00
Select.tsx จอสัมผัส (iOS/Android): แก้ครบตาม UX audit C1+M1–M6 + code review + งาน export PDF ที่ค้างใน working tree: 2026-09-14 18:04:40 +07:00
SessionBar.tsx 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) 2026-09-17 13:41:02 +07:00
UploadFileForm.tsx หน้ารายการ: แยกฟอร์มอัปโหลดเป็น section "เพิ่มเอกสาร" ของตัวเอง (เดิมอยู่ใต้หัว "เอกสาร" ดูเหมือนส่วนเดียวกับรายการ) + บอกชนิดไฟล์ที่รองรับใต้ฟอร์ม (PDF, JPG, PNG, TIFF — ไม่เกิน 100MB ต่อไฟล์) + การ์ดไฟล์กลับเป็นปุ่ม "Annotate" แทนลิงก์กางเต็มการ์ด (ย้อน M4 ตาม feedback) เป้าจิ้ม 36px + focus ring 2026-09-16 16:52:08 +07:00