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:
parent
6fd0c3174f
commit
eddcec28c7
60 changed files with 15055 additions and 0 deletions
109
tests/utils/annotation-display.test.ts
Normal file
109
tests/utils/annotation-display.test.ts
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
import { describe, expect, it } from "vitest";
|
||||
import type { AnnotationDTO, AnnotationData, AnnotationType } from "@/db/schema";
|
||||
import {
|
||||
DRAW_PENS,
|
||||
STAMP_PRESETS,
|
||||
annotationNote,
|
||||
annotationPreviewText,
|
||||
annotationTypeLabel,
|
||||
effectiveDrawOpacity,
|
||||
} from "@/utils/annotation-display";
|
||||
|
||||
const dto = (type: AnnotationType, data: AnnotationData): AnnotationDTO => ({
|
||||
id: "00000000-0000-0000-0000-000000000001",
|
||||
fileId: "00000000-0000-0000-0000-000000000002",
|
||||
pageIndex: 0,
|
||||
userId: "user-1",
|
||||
userName: "ทดสอบ",
|
||||
type,
|
||||
data,
|
||||
createdAt: "2026-01-01T00:00:00.000Z",
|
||||
updatedAt: "2026-01-01T00:00:00.000Z",
|
||||
resolved: false,
|
||||
resolvedAt: null,
|
||||
});
|
||||
|
||||
describe("annotationTypeLabel", () => {
|
||||
it("HIGHLIGHT ตาม style + fallback เมื่อไม่ระบุ style", () => {
|
||||
expect(
|
||||
annotationTypeLabel(dto("HIGHLIGHT", { text: "a", color: "#000", rects: [], style: "underline" })),
|
||||
).toBe("เส้นใต้");
|
||||
expect(annotationTypeLabel(dto("HIGHLIGHT", { text: "a", color: "#000", rects: [] }))).toBe("ไฮไลต์");
|
||||
});
|
||||
|
||||
it("ครบทุกชนิดที่เหลือ", () => {
|
||||
expect(annotationTypeLabel(dto("DRAW", { strokeColor: "#000", strokeWidth: 1, paths: [] }))).toBe("เส้นวาด");
|
||||
expect(annotationTypeLabel(dto("COMMENT", { position: { x: 0, y: 0 }, message: "m" }))).toBe("คอมเมนต์");
|
||||
expect(
|
||||
annotationTypeLabel(
|
||||
dto("SHAPE", { shape: "ellipse", rect: { x: 0, y: 0, width: 1, height: 1 }, strokeColor: "#000", strokeWidth: 1 }),
|
||||
),
|
||||
).toBe("รูปทรง (วงรี)");
|
||||
expect(annotationTypeLabel(dto("REDACTION", { rect: { x: 0, y: 0, width: 1, height: 1 } }))).toBe("ปิดพราง");
|
||||
expect(
|
||||
annotationTypeLabel(dto("TEXTBOX", { position: { x: 0, y: 0 }, text: "t", color: "#000", fontSize: 1 })),
|
||||
).toBe("กล่องข้อความ");
|
||||
expect(annotationTypeLabel(dto("STAMP", { label: "สำคัญ", position: { x: 0, y: 0 }, scale: 18 }))).toBe("ตรายาง");
|
||||
});
|
||||
});
|
||||
|
||||
describe("annotationPreviewText", () => {
|
||||
it("HIGHLIGHT ใช้ข้อความที่คลุม / ว่าง → placeholder", () => {
|
||||
expect(
|
||||
annotationPreviewText(dto("HIGHLIGHT", { text: "ข้อความที่คลุม", color: "#000", rects: [] })),
|
||||
).toBe("ข้อความที่คลุม");
|
||||
expect(annotationPreviewText(dto("HIGHLIGHT", { text: "", color: "#000", rects: [] }))).toBe(
|
||||
"(ไม่มีข้อความ)",
|
||||
);
|
||||
});
|
||||
|
||||
it("COMMENT → message, TEXTBOX → text, STAMP → label, DRAW → ข้อความตายตัว", () => {
|
||||
expect(
|
||||
annotationPreviewText(dto("COMMENT", { position: { x: 0, y: 0 }, message: "ดูจุดนี้" })),
|
||||
).toBe("ดูจุดนี้");
|
||||
expect(
|
||||
annotationPreviewText(dto("TEXTBOX", { position: { x: 0, y: 0 }, text: "หมายเหตุ", color: "#000", fontSize: 1 })),
|
||||
).toBe("หมายเหตุ");
|
||||
expect(
|
||||
annotationPreviewText(dto("STAMP", { label: "ตรวจสอบแล้ว", position: { x: 0, y: 0 }, scale: 18 })),
|
||||
).toBe("ตรวจสอบแล้ว");
|
||||
expect(
|
||||
annotationPreviewText(dto("DRAW", { strokeColor: "#000", strokeWidth: 1, paths: [{ x: 0, y: 0 }] })),
|
||||
).toBe("เส้นวาดอิสระ");
|
||||
});
|
||||
});
|
||||
|
||||
describe("annotationNote", () => {
|
||||
it("มี note → คืนค่านั้น / ไม่มี → ช่องว่าง", () => {
|
||||
expect(
|
||||
annotationNote(dto("DRAW", { strokeColor: "#000", strokeWidth: 1, paths: [], note: "เหตุผล" })),
|
||||
).toBe("เหตุผล");
|
||||
expect(annotationNote(dto("DRAW", { strokeColor: "#000", strokeWidth: 1, paths: [] }))).toBe("");
|
||||
});
|
||||
|
||||
it("COMMENT (ไม่มี field note) → ช่องว่าง", () => {
|
||||
expect(annotationNote(dto("COMMENT", { position: { x: 0, y: 0 }, message: "m" }))).toBe("");
|
||||
});
|
||||
});
|
||||
|
||||
describe("effectiveDrawOpacity", () => {
|
||||
it("opacity ที่เก็บชัดชนะเสมอ", () => {
|
||||
expect(effectiveDrawOpacity({ pen: "highlighter", opacity: 0.8 })).toBe(0.8);
|
||||
expect(effectiveDrawOpacity({ opacity: 0.3 })).toBe(0.3);
|
||||
});
|
||||
|
||||
it("ไม่มี opacity → fallback ตาม preset ของ pen", () => {
|
||||
expect(effectiveDrawOpacity({ pen: "highlighter" })).toBe(DRAW_PENS.highlighter.opacity);
|
||||
expect(effectiveDrawOpacity({ pen: "pencil" })).toBe(DRAW_PENS.pencil.opacity);
|
||||
});
|
||||
|
||||
it("ไม่มีทั้ง pen และ opacity → pencil", () => {
|
||||
expect(effectiveDrawOpacity({})).toBe(DRAW_PENS.pencil.opacity);
|
||||
});
|
||||
});
|
||||
|
||||
describe("presets ที่ sidebar/export ใช้ร่วมกัน", () => {
|
||||
it("STAMP_PRESETS มีค่าตาม D7 (preset ข้อความเท่านั้น)", () => {
|
||||
expect(STAMP_PRESETS).toEqual(["ตรวจสอบแล้ว", "สำคัญ"]);
|
||||
});
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue