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
55
app/api/annotations/export-csv/route.ts
Normal file
55
app/api/annotations/export-csv/route.ts
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
import { asc, eq } from "drizzle-orm";
|
||||
|
||||
import { db } from "@/db";
|
||||
import { annotations, files, toAnnotationDTO } from "@/db/schema";
|
||||
import { annotationNote, annotationPreviewText, annotationTypeLabel } from "@/utils/annotation-display";
|
||||
import { csvField } from "@/utils/csv";
|
||||
import { encodeRFC5987 } from "@/utils/pdf-export";
|
||||
|
||||
// Export สรุปหมายเหตุเป็น CSV (F16/D3) — ข้อมูลเดียวกับหน้าสรุป (F15)
|
||||
// ขึ้นต้นด้วย UTF-8 BOM เพื่อให้ Excel ภาษาไทยอ่านถูกต้อง (RFC 4180: ครอบ field
|
||||
// ด้วย double quote, ขึ้นบรรทัดใหม่ด้วย CRLF)
|
||||
|
||||
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 async function GET(req: Request) {
|
||||
const fileId = new URL(req.url).searchParams.get("fileId") ?? "";
|
||||
if (!UUID_RE.test(fileId)) {
|
||||
return new Response("fileId ไม่ถูกต้อง", { status: 400 });
|
||||
}
|
||||
|
||||
const [file] = await db.select().from(files).where(eq(files.id, fileId));
|
||||
if (!file) return new Response("ไม่พบไฟล์นี้", { status: 404 });
|
||||
|
||||
const rows = await db
|
||||
.select()
|
||||
.from(annotations)
|
||||
.where(eq(annotations.fileId, fileId))
|
||||
.orderBy(asc(annotations.pageIndex), asc(annotations.createdAt));
|
||||
|
||||
const header = ["หน้า", "ชนิด", "ผู้เขียน", "ข้อความ", "โน้ต", "สร้างเมื่อ"];
|
||||
const lines = rows.map((row) => {
|
||||
const a = toAnnotationDTO(row);
|
||||
return [
|
||||
String(a.pageIndex + 1),
|
||||
annotationTypeLabel(a),
|
||||
a.userName,
|
||||
annotationPreviewText(a),
|
||||
annotationNote(a),
|
||||
new Date(a.createdAt).toLocaleString("th-TH", { timeZone: "Asia/Bangkok" }),
|
||||
]
|
||||
.map(csvField)
|
||||
.join(",");
|
||||
});
|
||||
// = UTF-8 BOM — ต้องเป็นตัวแรกสุดของไฟล์เสมอ
|
||||
const csv = `${[header.map(csvField).join(","), ...lines].join("\r\n")}\r\n`;
|
||||
|
||||
const filename = encodeRFC5987(`${file.title}-summary.csv`);
|
||||
return new Response(csv, {
|
||||
headers: {
|
||||
"content-type": "text/csv; charset=utf-8",
|
||||
"content-disposition": `attachment; filename="summary.csv"; filename*=UTF-8''${filename}`,
|
||||
},
|
||||
});
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue