Add application codebase: PDF annotation for e-Court

Next.js 14 + Drizzle/PostgreSQL + SSE realtime sync — highlight,
freehand, comment, bookmark + ฟีเจอร์ต่อยอด F1–F19 (edit/move, undo/redo,
shapes, redaction, export PDF/CSV, reply, resolve, search, summary,
textbox, stamp, version history)

Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
DESKTOP-1R2VSQH\Lenovo ThinkPad E490 2026-09-10 14:29:40 +07:00
parent 6fd0c3174f
commit eddcec28c7
60 changed files with 15055 additions and 0 deletions

36
tests/utils/csv.test.ts Normal file
View file

@ -0,0 +1,36 @@
import { describe, expect, it } from "vitest";
import { csvField } from "@/utils/csv";
describe("csvField", () => {
it("ครอบ double quote ตาม RFC 4180", () => {
expect(csvField("ทดสอบ")).toBe('"ทดสอบ"');
});
it("escape double quote ในค่าเป็น \"\"", () => {
expect(csvField('พูดว่า "สวัสดี"')).toBe('"พูดว่า ""สวัสดี"""');
});
it("ตัวเลขหน้าไม่ถูกนำหน้าอะไร", () => {
expect(csvField("1")).toBe('"1"');
expect(csvField("12")).toBe('"12"');
});
it("FIX-6: ขึ้นต้นด้วย = + - @ ต้องนำหน้าด้วย ' กันสูตร Excel", () => {
expect(csvField("=1+1")).toBe('"\'=1+1"');
expect(csvField("+SUM(A1:A2)")).toBe('"\'+SUM(A1:A2)"');
expect(csvField("-2+3")).toBe('"\'-2+3"');
expect(csvField("@SUM(A1)")).toBe('"\'@SUM(A1)"');
});
it("FIX-6: ขึ้นต้นด้วย TAB/CR ต้องนำหน้าด้วย ' เช่นกัน", () => {
expect(csvField("\t=cmd")).toBe('"\'\t=cmd"');
expect(csvField("\r=cmd")).toBe('"\'\r=cmd"');
});
it("อักขระพิเศษที่ไม่ใช่จุดเริ่มสูตรไม่ถูกนำหน้า", () => {
expect(csvField("1+1")).toBe('"1+1"');
expect(csvField("a=b")).toBe('"a=b"');
expect(csvField("ราคา -50 บาท")).toBe('"ราคา -50 บาท"');
});
});