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 ผ่าน
All checks were successful
Deploy eCourt Note Web to Server / Validate Database Schema (Drizzle) (push) Successful in 3s
Deploy eCourt Note Web to Server / Build Web Docker Image (push) Successful in 43s
Deploy eCourt Note Web to Server / Deploy eCourt Note Web to Server (push) Successful in 3s
Deploy eCourt Note Web to Server / Notify Deployment Status (push) Successful in 1s

Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
Your Name 2026-09-17 17:02:31 +07:00
parent 8ba911a119
commit e0c78d68fe
11 changed files with 469 additions and 38 deletions

View file

@ -114,8 +114,13 @@ import {
type Size,
} from "@/utils/coordinates";
import type { OacUser } from "@/utils/user";
import {
MAX_BOOKMARK_NOTE_CHARS,
MAX_BOOKMARK_TITLE_CHARS,
} from "@/utils/bookmark-limits";
import { fileKindFromUrl, imageFormatFromUrl } from "@/utils/file-kind";
import { withBasePath } from "@/utils/base-path";
import { formatThaiDate } from "@/utils/date-format";
import { decodeTiffPages } from "@/utils/tiff-client";
import {
TEXTBOX_DEFAULT_WIDTH_PCT,
@ -196,10 +201,13 @@ const HIGHLIGHT_COLORS = ["#fde047", "#86efac", "#93c5fd", "#f9a8d4", "#fdba74"]
/** ชื่อ bookmark ที่มาจากข้อความ (คลุม/ข้อความรายการ) — ล้างขยะของ textLayer ไทย
* (sanitizePdfText ครอบด้วยข้อความจาก DB เก่าที่ยังไม่ผ่านการล้าง) ยุบ whitespace
* และตัดยาวไม่เกิน 120 ตัวอักษร */
* และตัดยาวไม่เกิน MAX_BOOKMARK_TITLE_CHARS ตัวอักษร (รวม … ที่ต่อท้าย —
* เดิม slice 120 แล้วต่อ … กลายเป็น 121 ตัว โดน limit ของ server ตีกลับตอนบันทึก) */
function normalizeBookmarkTitle(s: string): string {
const t = sanitizePdfText(s).replace(/\s+/g, " ").trim();
return t.length > 120 ? `${t.slice(0, 120)}…` : t;
return t.length > MAX_BOOKMARK_TITLE_CHARS
? `${t.slice(0, MAX_BOOKMARK_TITLE_CHARS - 1)}…`
: t;
}
/** ความสูง sticky header ของ viewer — ใช้เว้นระยะเวลาเลื่อนหน้าไม่ให้เป้าหมายถูก header บัง */
@ -492,12 +500,7 @@ function ReplyBox({
<p className="text-[11px] font-medium text-slate-600">
{r.userName}
<span className="ml-1 font-normal text-slate-400" suppressHydrationWarning>
{new Date(r.createdAt).toLocaleString("th-TH", {
hour: "2-digit",
minute: "2-digit",
day: "2-digit",
month: "short",
})}
{formatThaiDate(r.createdAt, true)}
</span>
</p>
<p className="whitespace-pre-wrap break-words text-[11px]">{r.message}</p>
@ -1075,7 +1078,7 @@ function PdfPage({
</p>
)}
<p className="mt-1 text-[11px] text-slate-400" suppressHydrationWarning>
{new Date(a.createdAt).toLocaleString("th-TH")}
{formatThaiDate(a.createdAt, true)}
</p>
{/* F10/A5: toggle จัดการแล้ว — ใครก็ได้ (D6) */}
<button
@ -3550,6 +3553,7 @@ export default function PDFViewer({
<input
autoFocus
onFocus={(e) => e.target.select()} // เลือกข้อความ default ทั้งหมด — พิมพ์ทับ/Enter ยอมรับได้ทันที
maxLength={MAX_BOOKMARK_TITLE_CHARS}
value={bookmarkComposer.title}
onChange={(e) =>
setBookmarkComposer({ ...bookmarkComposer, title: e.target.value })
@ -3564,6 +3568,7 @@ export default function PDFViewer({
/>
<textarea
rows={2}
maxLength={MAX_BOOKMARK_NOTE_CHARS}
value={bookmarkComposer.note}
onChange={(e) =>
setBookmarkComposer({ ...bookmarkComposer, note: e.target.value })